@aiderdesk/aiderdesk 0.64.0 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/cli.js +1 -0
- package/out/{open-telemetry-CcefKvbB.js → open-telemetry-baOvr6sK.js} +1 -1
- package/out/renderer/assets/{arc-DoIK-bD2.js → arc-BnPy4nhx.js} +1 -1
- package/out/renderer/assets/{architectureDiagram-Q4EWVU46-B8_dgBXp.js → architectureDiagram-3BPJPVTR-_SwpsbSs.js} +11 -9
- package/out/renderer/assets/{blockDiagram-DXYQGD6D-BDOvGPDN.js → blockDiagram-GPEHLZMM-D8Bc_xCY.js} +218 -30
- package/out/renderer/assets/{c4Diagram-AHTNJAMY-1ABZnJ2v.js → c4Diagram-AAUBKEIU-Ddic5IpI.js} +2 -2
- package/out/renderer/assets/{channel-Cr_H2zdE.js → channel-I2sEi2rW.js} +1 -1
- package/out/renderer/assets/{chunk-EDXVE4YY-Dt80V_EG.js → chunk-2J33WTMH-Cqrw7IHw.js} +1 -1
- package/out/renderer/assets/{chunk-4BX2VUAB-d88VZY9C.js → chunk-4BX2VUAB-DKY4go9E.js} +1 -1
- package/out/renderer/assets/{chunk-55IACEB6-BO1oJBQV.js → chunk-55IACEB6-COO6Gc0F.js} +1 -1
- package/out/renderer/assets/{chunk-4TB4RGXK-DLcMuHVI.js → chunk-727SXJPM-Bu8zhvGn.js} +245 -149
- package/out/renderer/assets/{chunk-OYMX7WX6-DBFhtMcs.js → chunk-AQP2D5EJ-JHXEk1U1.js} +92 -78
- package/out/renderer/assets/{chunk-FMBD7UC4-D5MNbIWZ.js → chunk-FMBD7UC4-BqbYNISq.js} +1 -1
- package/out/renderer/assets/{chunk-YZCP3GAM-gAcMGuhT.js → chunk-ND2GUHAM-D29ZgnDp.js} +1 -1
- package/out/renderer/assets/{chunk-QZHKN3VN-Bxwt_pyh.js → chunk-QZHKN3VN-B-pbtAbR.js} +1 -1
- package/out/renderer/assets/{classDiagram-6PBFFD2Q-B7lgamMP.js → classDiagram-4FO5ZUOK-B7_tIdbP.js} +6 -6
- package/out/renderer/assets/{classDiagram-v2-HSJHXN6E-B7lgamMP.js → classDiagram-v2-Q7XG4LA2-B7_tIdbP.js} +6 -6
- package/out/renderer/assets/{cose-bilkent-S5V4N54A-BZNBIG2x.js → cose-bilkent-S5V4N54A-CGNgxRV6.js} +1 -1
- package/out/renderer/assets/{dagre-KV5264BT-C3hXUNb-.js → dagre-BM42HDAG-DB-tI7Ml.js} +17 -6
- package/out/renderer/assets/{diagram-MMDJMWI5-BcI1Ek4N.js → diagram-2AECGRRQ-Cdwrgy7R.js} +3 -5
- package/out/renderer/assets/{diagram-5BDNPKRD-DNh45EqP.js → diagram-5GNKFQAL-BnvZ78_N.js} +4 -6
- package/out/renderer/assets/diagram-KO2AKTUF-VUrfwRCk.js +632 -0
- package/out/renderer/assets/{diagram-TYMM5635-DuHcW-s7.js → diagram-LMA3HP47-_Vo8GrFC.js} +3 -5
- package/out/renderer/assets/{diagram-G4DWMVQ6-8lhqJfPk.js → diagram-OG6HWLK6-CoLz9kio.js} +4 -6
- package/out/renderer/assets/{erDiagram-SMLLAGMA-I6Q9HYdF.js → erDiagram-TEJ5UH35-B3Bg3gW1.js} +4 -4
- package/out/renderer/assets/{flowDiagram-DWJPFMVM-BzRjtX5C.js → flowDiagram-I6XJVG4X-BgyAk0Xe.js} +6 -6
- package/out/renderer/assets/{ganttDiagram-T4ZO3ILL-DVkem_IA.js → ganttDiagram-6RSMTGT7-NnVRwQPs.js} +7 -1
- package/out/renderer/assets/{gitGraphDiagram-UUTBAWPF-BYpvdMpK.js → gitGraphDiagram-PVQCEYII-C4vrRnDw.js} +4 -6
- package/out/renderer/assets/{graph-CAtr5PoG.js → graph-BZvTCUpv.js} +490 -135
- package/out/renderer/assets/{index-CNL53LoL.js → index-P63PgYUG.js} +12163 -8228
- package/out/renderer/assets/{index-Duw36zwk.css → index-zdiQSGqQ.css} +135 -27
- package/out/renderer/assets/{infoDiagram-42DDH7IO-BcmBthOY.js → infoDiagram-5YYISTIA-zTriWVJJ.js} +3 -5
- package/out/renderer/assets/{ishikawaDiagram-UXIWVN3A-moTWny-V.js → ishikawaDiagram-YF4QCWOH-DHO6Yeea.js} +1 -1
- package/out/renderer/assets/{journeyDiagram-VCZTEJTY-DOW8zaZt.js → journeyDiagram-JHISSGLW-CZsRcg2X.js} +4 -4
- package/out/renderer/assets/{kanban-definition-6JOO6SKY-DpJjTob4.js → kanban-definition-UN3LZRKU-DSpAeh8p.js} +2 -2
- package/out/renderer/assets/{layout-BvH51Ui9.js → layout-B5A8fT-Z.js} +459 -32
- package/out/renderer/assets/{mindmap-definition-QFDTVHPH-DggFFNHq.js → mindmap-definition-RKZ34NQL-Bns9Ab8p.js} +3 -3
- package/out/renderer/assets/{pieDiagram-DEJITSTG-BED4dnMF.js → pieDiagram-4H26LBE5-BX36DY7W.js} +4 -6
- package/out/renderer/assets/{quadrantDiagram-34T5L4WZ-RpQ3qNU5.js → quadrantDiagram-W4KKPZXB-DuD2qSni.js} +22 -20
- package/out/renderer/assets/{requirementDiagram-MS252O5E-VQt4zBMB.js → requirementDiagram-4Y6WPE33-Th2uknlw.js} +3 -3
- package/out/renderer/assets/{sankeyDiagram-XADWPNL6-DywR7qAk.js → sankeyDiagram-5OEKKPKP-DP3WODC7.js} +80 -11
- package/out/renderer/assets/{sequenceDiagram-FGHM5R23-CVPfZD4e.js → sequenceDiagram-3UESZ5HK-BvpGEq-v.js} +21 -9
- package/out/renderer/assets/{stateDiagram-FHFEXIEX-BrH8Q8ZG.js → stateDiagram-AJRCARHV-Btrfma35.js} +6 -8
- package/out/renderer/assets/{stateDiagram-v2-QKLJ7IA2-BTWk2K0H.js → stateDiagram-v2-BHNVJYJU-v-8JiozM.js} +4 -4
- package/out/renderer/assets/{timeline-definition-GMOUNBTQ-DwDUCrTb.js → timeline-definition-PNZ67QCA-prZy7cGx.js} +2 -2
- package/out/renderer/assets/{vennDiagram-DHZGUBPP-Bjvr7yGM.js → vennDiagram-CIIHVFJN-XCVp5OjS.js} +1 -1
- package/out/renderer/assets/{wardley-RL74JXVD-Bo-sW7uQ.js → wardley-L42UT6IY-FZSNLjCs.js} +25605 -19118
- package/out/renderer/assets/{wardleyDiagram-NUSXRM2D-DRW_1PCJ.js → wardleyDiagram-YWT4CUSO-Cla_7ryL.js} +112 -38
- package/out/renderer/assets/worker-CfJUABHG.js +12626 -0
- package/out/renderer/assets/{xychartDiagram-5P7HB3ND-Ds-qS4nC.js → xychartDiagram-2RQKCTM6-CKvwGbhk.js} +1 -1
- package/out/renderer/index.html +2 -2
- package/out/renderer/progress.html +4 -48
- package/out/resources/connector/connector.py +5 -0
- package/out/resources/mcp-server/aider-desk-mcp-server.js +1051 -501
- package/out/resources/prompts/workflow.hbs +2 -2
- package/out/runner.js +2169 -327
- package/package.json +42 -21
- package/scripts/generate-package.mjs +10 -2
- package/out/renderer/assets/_baseUniq-C6Q8LpuQ.js +0 -381
- package/out/renderer/assets/clone-DKkqtIT8.js +0 -8
- package/out/renderer/assets/min-CowxrbD6.js +0 -41
package/out/runner.js
CHANGED
|
@@ -31,22 +31,25 @@ const node_sqlite = require("node:sqlite");
|
|
|
31
31
|
const path = require("path");
|
|
32
32
|
const os = require("os");
|
|
33
33
|
const http = require("http");
|
|
34
|
-
const gpt4o = require("gpt-tokenizer/model/gpt-4o");
|
|
35
34
|
const istextorbinary = require("istextorbinary");
|
|
36
35
|
const child_process = require("child_process");
|
|
36
|
+
const util = require("util");
|
|
37
37
|
const treeKill = require("tree-kill");
|
|
38
|
+
const ai = require("ai");
|
|
38
39
|
const glob = require("glob");
|
|
39
40
|
const fs$1 = require("fs");
|
|
40
|
-
const util = require("util");
|
|
41
41
|
const dotenvx = require("@dotenvx/dotenvx");
|
|
42
42
|
const YAML = require("yaml");
|
|
43
43
|
const posthogNode = require("posthog-node");
|
|
44
44
|
const langfuseVercel = require("langfuse-vercel");
|
|
45
|
+
const otel = require("@posthog/ai/otel");
|
|
45
46
|
const simpleGit = require("simple-git");
|
|
46
47
|
const filenamifyImport = require("filenamify");
|
|
47
48
|
const slugify = require("slugify");
|
|
49
|
+
const AdmZip = require("adm-zip");
|
|
48
50
|
const Turndown = require("turndown");
|
|
49
51
|
const cheerio = require("cheerio");
|
|
52
|
+
const gpt4o = require("gpt-tokenizer/model/gpt-4o");
|
|
50
53
|
const uuid = require("uuid");
|
|
51
54
|
const index_js = require("@modelcontextprotocol/sdk/client/index.js");
|
|
52
55
|
const stdio_js = require("@modelcontextprotocol/sdk/client/stdio.js");
|
|
@@ -63,7 +66,6 @@ const debounce = require("lodash/debounce.js");
|
|
|
63
66
|
const crypto = require("crypto");
|
|
64
67
|
const undici = require("undici");
|
|
65
68
|
const globalAgent = require("global-agent");
|
|
66
|
-
const ai = require("ai");
|
|
67
69
|
const fileType = require("file-type");
|
|
68
70
|
const anthropic = require("@ai-sdk/anthropic");
|
|
69
71
|
const azure = require("@ai-sdk/azure");
|
|
@@ -229,14 +231,16 @@ const TASKS_TOOL_GET_TASK_MESSAGE = "get_task_message";
|
|
|
229
231
|
const TASKS_TOOL_CREATE_TASK = "create_task";
|
|
230
232
|
const TASKS_TOOL_DELETE_TASK = "delete_task";
|
|
231
233
|
const TASKS_TOOL_SEARCH_TASK = "search_task";
|
|
234
|
+
const TASKS_TOOL_RUN_PROMPT = "run_prompt";
|
|
232
235
|
const TASKS_TOOL_SEARCH_PARENT_TASK = "search_parent_task";
|
|
233
236
|
const TASKS_TOOL_DESCRIPTIONS = {
|
|
234
237
|
[TASKS_TOOL_LIST_TASKS]: "List all tasks in the current project. Returns basic information for each task including id, name, and creation/update timestamps. Use this to get an overview of all available tasks before performing specific task operations.",
|
|
235
238
|
[TASKS_TOOL_GET_TASK]: "Get comprehensive details about a specific task by its ID. Returns task metadata, current state, list of context files with their read-only status, and the total count of context messages. Use this to understand a task's configuration and context before working with it or its messages.",
|
|
236
239
|
[TASKS_TOOL_GET_TASK_MESSAGE]: "Retrieve a specific message from a task's conversation history by message index and task ID. The first message (index 0) is always the user's initial prompt, and subsequent messages alternate between user and assistant. Use this to examine the conversation flow, understand previous interactions, or extract specific information from the task history.",
|
|
237
|
-
[TASKS_TOOL_CREATE_TASK]: "Create a new task in the current project with an initial prompt. Optionally
|
|
240
|
+
[TASKS_TOOL_CREATE_TASK]: "Create a new task in the current project with an initial prompt. Optionally use user-provided agentProfileId to use a different agent, a modelId to override the model, or a parentTaskId (if available) to create a subtask of another task. The new task will start with the provided prompt as its first user message. Use this to begin new work streams, separate different aspects of a project, or break down complex tasks into manageable subtasks. Use execute to create a task and wait for the task to be finished. Use executeInBackground to create a task in the background without waiting for the task to be finished.",
|
|
238
241
|
[TASKS_TOOL_DELETE_TASK]: "Permanently delete a task and all its associated data including messages, context files, and metadata. This action cannot be undone. Note that you cannot delete the currently active task. Use this to clean up completed or abandoned tasks, but be cautious as this removes all task history permanently.",
|
|
239
242
|
[TASKS_TOOL_SEARCH_TASK]: "Search content within a specific task using semantic search. Use natural language queries with 2-5 descriptive words including key concepts and context. Searches through task conversation history and context files. Use this to find relevant information, discussions, or code snippets within a task.",
|
|
243
|
+
[TASKS_TOOL_RUN_PROMPT]: "Run a prompt on an existing task. The task must already exist (use create_task first if needed). Use this to send additional instructions to a task, continue work on an existing task, or delegate follow-up work. By default, waits for the prompt to complete before returning. Use executeInBackground to run without waiting.",
|
|
240
244
|
[TASKS_TOOL_SEARCH_PARENT_TASK]: "Search content within parent task using semantic search. Use natural language queries with 2-5 descriptive words including key concepts and context. Automatically searches parent task conversation history and context files."
|
|
241
245
|
};
|
|
242
246
|
const WorktreeSchema = zod.z.object({
|
|
@@ -255,6 +259,13 @@ const MergeStateSchema = zod.z.object({
|
|
|
255
259
|
});
|
|
256
260
|
const AIDER_MODES = ["code", "ask", "architect", "context"];
|
|
257
261
|
const AIDER_COMMANDS = ["commit", "map", "map-refresh", "tokens", "test"];
|
|
262
|
+
var AutonomyMode = /* @__PURE__ */ ((AutonomyMode2) => {
|
|
263
|
+
AutonomyMode2["Manual"] = "manual";
|
|
264
|
+
AutonomyMode2["Guided"] = "guided";
|
|
265
|
+
AutonomyMode2["Autonomous"] = "autonomous";
|
|
266
|
+
return AutonomyMode2;
|
|
267
|
+
})(AutonomyMode || {});
|
|
268
|
+
const DEFAULT_AUTONOMY_MODE = "guided";
|
|
258
269
|
var DiffViewMode = /* @__PURE__ */ ((DiffViewMode2) => {
|
|
259
270
|
DiffViewMode2["SideBySide"] = "side-by-side";
|
|
260
271
|
DiffViewMode2["Unified"] = "unified";
|
|
@@ -297,9 +308,8 @@ const ProjectSettingsSchema = zod.z.object({
|
|
|
297
308
|
reasoningEffort: zod.z.string().optional(),
|
|
298
309
|
thinkingTokens: zod.z.string().optional(),
|
|
299
310
|
currentMode: zod.z.string(),
|
|
300
|
-
contextCompactingThreshold: zod.z.number().optional(),
|
|
301
311
|
weakModelLocked: zod.z.boolean().optional(),
|
|
302
|
-
|
|
312
|
+
autonomyModeLocked: zod.z.boolean().optional(),
|
|
303
313
|
updatedFilesGroupMode: zod.z.enum(["grouped", "flat"]).default("flat"),
|
|
304
314
|
disabledRuleFiles: zod.z.array(zod.z.string()).default([]),
|
|
305
315
|
contextSidebarSectionsOrder: zod.z.array(zod.z.string()).default([]),
|
|
@@ -335,8 +345,15 @@ var MemoryEmbeddingProvider = /* @__PURE__ */ ((MemoryEmbeddingProvider2) => {
|
|
|
335
345
|
var ContextCompactionType = /* @__PURE__ */ ((ContextCompactionType2) => {
|
|
336
346
|
ContextCompactionType2["Compact"] = "compact";
|
|
337
347
|
ContextCompactionType2["Handoff"] = "handoff";
|
|
348
|
+
ContextCompactionType2["Smart"] = "smart";
|
|
338
349
|
return ContextCompactionType2;
|
|
339
350
|
})(ContextCompactionType || {});
|
|
351
|
+
var FileWatchMode = /* @__PURE__ */ ((FileWatchMode2) => {
|
|
352
|
+
FileWatchMode2["Auto"] = "auto";
|
|
353
|
+
FileWatchMode2["Native"] = "native";
|
|
354
|
+
FileWatchMode2["Polling"] = "polling";
|
|
355
|
+
return FileWatchMode2;
|
|
356
|
+
})(FileWatchMode || {});
|
|
340
357
|
var MemoryEmbeddingProgressPhase = /* @__PURE__ */ ((MemoryEmbeddingProgressPhase2) => {
|
|
341
358
|
MemoryEmbeddingProgressPhase2["Idle"] = "idle";
|
|
342
359
|
MemoryEmbeddingProgressPhase2["LoadingModel"] = "loading-model";
|
|
@@ -370,7 +387,7 @@ const TaskDataSchema = zod.z.object({
|
|
|
370
387
|
lastMergeState: MergeStateSchema.optional(),
|
|
371
388
|
aiderTotalCost: zod.z.number(),
|
|
372
389
|
agentTotalCost: zod.z.number(),
|
|
373
|
-
|
|
390
|
+
autonomyMode: zod.z.nativeEnum(AutonomyMode).optional(),
|
|
374
391
|
agentProfileId: zod.z.string().optional(),
|
|
375
392
|
provider: zod.z.string().optional(),
|
|
376
393
|
model: zod.z.string().optional(),
|
|
@@ -380,7 +397,7 @@ const TaskDataSchema = zod.z.object({
|
|
|
380
397
|
reasoningEffort: zod.z.string().optional(),
|
|
381
398
|
thinkingTokens: zod.z.string().optional(),
|
|
382
399
|
currentMode: zod.z.string().optional(),
|
|
383
|
-
|
|
400
|
+
contextCompactingThresholdTokens: zod.z.number().optional(),
|
|
384
401
|
weakModelLocked: zod.z.boolean().optional(),
|
|
385
402
|
handoff: zod.z.boolean().optional(),
|
|
386
403
|
lastAgentProviderMetadata: zod.z.unknown().optional(),
|
|
@@ -455,6 +472,20 @@ const extractTextContent = (content) => {
|
|
|
455
472
|
}
|
|
456
473
|
return "";
|
|
457
474
|
};
|
|
475
|
+
const extractImagesFromContent = (content) => {
|
|
476
|
+
if (!Array.isArray(content)) {
|
|
477
|
+
return void 0;
|
|
478
|
+
}
|
|
479
|
+
const images = content.filter((part) => part.type === "image").map((part) => {
|
|
480
|
+
const data = part.image;
|
|
481
|
+
if (typeof data !== "string") {
|
|
482
|
+
return void 0;
|
|
483
|
+
}
|
|
484
|
+
const mediaType = part.mediaType || "image/png";
|
|
485
|
+
return data.startsWith("data:") ? data : `data:${mediaType};base64,${data}`;
|
|
486
|
+
}).filter((v) => v !== void 0);
|
|
487
|
+
return images.length > 0 ? images : void 0;
|
|
488
|
+
};
|
|
458
489
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
459
490
|
const parseUsageReport = (model, report) => {
|
|
460
491
|
const sentMatch = report.match(/Tokens: ([\d.]+k?) sent/);
|
|
@@ -547,6 +578,7 @@ const extractProviderModel = (modelId) => {
|
|
|
547
578
|
const [providerId, ...modelParts] = modelId.split("/");
|
|
548
579
|
return [providerId, modelParts.join("/")];
|
|
549
580
|
};
|
|
581
|
+
const isUuid = (id) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);
|
|
550
582
|
const DEFAULT_PAGE_SIZE = 100;
|
|
551
583
|
class LogBuffer {
|
|
552
584
|
db = null;
|
|
@@ -793,6 +825,7 @@ const AIDER_DESK_CONNECTOR_DIR = path.join(AIDER_DESK_DATA_DIR, "aider-connector
|
|
|
793
825
|
const AIDER_DESK_MCP_SERVER_DIR = path.join(AIDER_DESK_DATA_DIR, "mcp-server");
|
|
794
826
|
const AIDER_DESK_BIN_DIR = path.join(AIDER_DESK_DATA_DIR, "bin");
|
|
795
827
|
const UV_EXECUTABLE = process.platform === "win32" ? path.join(AIDER_DESK_BIN_DIR, "uv.exe") : path.join(AIDER_DESK_BIN_DIR, "uv");
|
|
828
|
+
const RIPGREP_BINARY_PATH = process.platform === "win32" ? path.join(AIDER_DESK_BIN_DIR, "rg.exe") : path.join(AIDER_DESK_BIN_DIR, "rg");
|
|
796
829
|
const SERVER_PORT = process.env.AIDER_DESK_PORT ? parseInt(process.env.AIDER_DESK_PORT) : 24337;
|
|
797
830
|
const PID_FILES_DIR = path.join(AIDER_DESK_DATA_DIR, "aider-processes");
|
|
798
831
|
const AIDER_DESK_DIR = ".aider-desk";
|
|
@@ -816,7 +849,9 @@ const AIDER_DESK_MEMORY_FILE = path.join(AIDER_DESK_DATA_DIR, "memory.db");
|
|
|
816
849
|
const EXTENSIONS_REPOS_CACHE_DIR = path.join(AIDER_DESK_CACHE_DIR, "extensions");
|
|
817
850
|
const POSTHOG_PUBLIC_API_KEY = "phc_AF4zkjrcziXLh8PBFsRSvVr4VZ38p3ezsdX0KDYuElI";
|
|
818
851
|
const POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
819
|
-
process.env.AIDER_DESK_HEADLESS === "true";
|
|
852
|
+
const HEADLESS_MODE = process.env.AIDER_DESK_HEADLESS === "true";
|
|
853
|
+
const APP_TYPE = process.env.AIDER_DESK_APP_TYPE || (HEADLESS_MODE ? "docker" : "electron");
|
|
854
|
+
process.env.AIDER_DESK_DISABLE_MENU === "true";
|
|
820
855
|
const AUTH_USERNAME = process.env.AIDER_DESK_USERNAME;
|
|
821
856
|
const AUTH_PASSWORD = process.env.AIDER_DESK_PASSWORD;
|
|
822
857
|
const CORS_ALLOWED_ORIGINS = process.env.AIDER_DESK_CORS_ALLOWED_ORIGINS;
|
|
@@ -866,6 +901,9 @@ if (process.env.NODE_ENV !== "production" || process.env.AIDER_DESK_HEADLESS ===
|
|
|
866
901
|
const initEventLogging = (eventManager) => {
|
|
867
902
|
eventTransport.setEventManager(eventManager);
|
|
868
903
|
};
|
|
904
|
+
const isAutoApprove = (task) => {
|
|
905
|
+
return task.task.autonomyMode !== AutonomyMode.Manual;
|
|
906
|
+
};
|
|
869
907
|
class ApprovalManager {
|
|
870
908
|
constructor(task, profile) {
|
|
871
909
|
this.task = task;
|
|
@@ -875,7 +913,7 @@ class ApprovalManager {
|
|
|
875
913
|
async handleToolApproval(toolName, input, key, text, subject) {
|
|
876
914
|
const extensionResult = await this.task.dispatchExtensionEvent("onToolApproval", { toolName, input });
|
|
877
915
|
if (extensionResult.blocked) {
|
|
878
|
-
return [false, void 0];
|
|
916
|
+
return [false, typeof extensionResult.blocked === "string" ? extensionResult.blocked : void 0];
|
|
879
917
|
}
|
|
880
918
|
if (extensionResult.allowed) {
|
|
881
919
|
return [true, void 0];
|
|
@@ -893,7 +931,7 @@ class ApprovalManager {
|
|
|
893
931
|
key = extensionResult.key;
|
|
894
932
|
text = extensionResult.text;
|
|
895
933
|
subject = extensionResult.subject;
|
|
896
|
-
if (this.task
|
|
934
|
+
if (isAutoApprove(this.task)) {
|
|
897
935
|
return [true, void 0];
|
|
898
936
|
}
|
|
899
937
|
const isApprovedFromSet = this.alwaysApproveForRunKeys.has(key) || (this.profile.toolApprovals[key] || ToolApprovalState.Always) === ToolApprovalState.Always;
|
|
@@ -1474,6 +1512,7 @@ const AVAILABLE_PROVIDERS = [
|
|
|
1474
1512
|
"lmstudio",
|
|
1475
1513
|
"minimax",
|
|
1476
1514
|
"mistral",
|
|
1515
|
+
"neuralwatt",
|
|
1477
1516
|
"ollama",
|
|
1478
1517
|
"openai",
|
|
1479
1518
|
"openai-compatible",
|
|
@@ -1519,6 +1558,7 @@ const isOpenCodeProvider = (provider) => provider.name === "opencode";
|
|
|
1519
1558
|
const isZaiPlanProvider = (provider) => provider.name === "zai-plan";
|
|
1520
1559
|
const isMinimaxProvider = (provider) => provider.name === "minimax";
|
|
1521
1560
|
const isMistralProvider = (provider) => provider.name === "mistral";
|
|
1561
|
+
const isNeuralwattProvider = (provider) => provider.name === "neuralwatt";
|
|
1522
1562
|
const isSyntheticProvider = (provider) => provider.name === "synthetic";
|
|
1523
1563
|
const DEFAULT_PROVIDER_MODELS = {
|
|
1524
1564
|
"alibaba-plan": "qwen3-coder-plus",
|
|
@@ -1539,7 +1579,8 @@ const DEFAULT_PROVIDER_MODELS = {
|
|
|
1539
1579
|
synthetic: "hf:zai-org/GLM-4.7",
|
|
1540
1580
|
"zai-plan": "glm-5.1",
|
|
1541
1581
|
minimax: "MiniMax-M2.7",
|
|
1542
|
-
mistral: "mistral-large-latest"
|
|
1582
|
+
mistral: "mistral-large-latest",
|
|
1583
|
+
neuralwatt: "claude-sonnet-4-6"
|
|
1543
1584
|
};
|
|
1544
1585
|
const DEFAULT_AIDER_MAIN_MODEL = `anthropic/${DEFAULT_PROVIDER_MODELS.anthropic}`;
|
|
1545
1586
|
const DEFAULT_AGENT_PROFILE_ID = "default";
|
|
@@ -1548,7 +1589,7 @@ const DEFAULT_AGENT_PROFILE = {
|
|
|
1548
1589
|
name: "Default Agent",
|
|
1549
1590
|
provider: "anthropic",
|
|
1550
1591
|
model: DEFAULT_PROVIDER_MODELS.anthropic,
|
|
1551
|
-
maxIterations:
|
|
1592
|
+
maxIterations: 0,
|
|
1552
1593
|
minTimeBetweenToolCalls: 0,
|
|
1553
1594
|
toolApprovals: {
|
|
1554
1595
|
// aider tools
|
|
@@ -1869,7 +1910,8 @@ const getDefaultProviderParams = (providerName) => {
|
|
|
1869
1910
|
name: "openai-compatible",
|
|
1870
1911
|
apiKey: "",
|
|
1871
1912
|
baseUrl: "",
|
|
1872
|
-
reasoningEffort: ReasoningEffort.None
|
|
1913
|
+
reasoningEffort: ReasoningEffort.None,
|
|
1914
|
+
trackTokenUsage: true
|
|
1873
1915
|
};
|
|
1874
1916
|
break;
|
|
1875
1917
|
case "litellm":
|
|
@@ -1943,6 +1985,12 @@ const getDefaultProviderParams = (providerName) => {
|
|
|
1943
1985
|
apiKey: ""
|
|
1944
1986
|
};
|
|
1945
1987
|
break;
|
|
1988
|
+
case "neuralwatt":
|
|
1989
|
+
provider = {
|
|
1990
|
+
name: "neuralwatt",
|
|
1991
|
+
apiKey: ""
|
|
1992
|
+
};
|
|
1993
|
+
break;
|
|
1946
1994
|
case "gemini-cli":
|
|
1947
1995
|
provider = {
|
|
1948
1996
|
name: "gemini-cli",
|
|
@@ -1984,7 +2032,7 @@ class TelemetryManager {
|
|
|
1984
2032
|
}
|
|
1985
2033
|
async init() {
|
|
1986
2034
|
try {
|
|
1987
|
-
await Promise.resolve().then(() => require("./open-telemetry-
|
|
2035
|
+
await Promise.resolve().then(() => require("./open-telemetry-baOvr6sK.js"));
|
|
1988
2036
|
const app = getElectronApp();
|
|
1989
2037
|
this.client = new posthogNode.PostHog(POSTHOG_PUBLIC_API_KEY, {
|
|
1990
2038
|
host: POSTHOG_HOST
|
|
@@ -1994,7 +2042,8 @@ class TelemetryManager {
|
|
|
1994
2042
|
distinctId: this.distinctId,
|
|
1995
2043
|
properties: {
|
|
1996
2044
|
os: process.platform,
|
|
1997
|
-
version: app?.getVersion()
|
|
2045
|
+
version: app?.getVersion(),
|
|
2046
|
+
appType: APP_TYPE
|
|
1998
2047
|
}
|
|
1999
2048
|
});
|
|
2000
2049
|
} catch (error) {
|
|
@@ -2064,7 +2113,7 @@ class TelemetryManager {
|
|
|
2064
2113
|
useTodoTools: profile.useTodoTools,
|
|
2065
2114
|
includeContextFiles: profile.includeContextFiles,
|
|
2066
2115
|
includeRepoMap: profile.includeRepoMap,
|
|
2067
|
-
|
|
2116
|
+
autonomyMode: task?.autonomyMode ?? "guided",
|
|
2068
2117
|
enabledMcpServersCount: profile.enabledServers.length,
|
|
2069
2118
|
totalMcpServersCount: Object.keys(this.store.getSettings().mcpServers).length
|
|
2070
2119
|
}
|
|
@@ -2157,6 +2206,24 @@ const getLangfuseEnvironmentVariables = (baseDir, settings) => {
|
|
|
2157
2206
|
LANGFUSE_HOST: getEffectiveEnvironmentVariable("LANGFUSE_HOST", settings, baseDir)?.value
|
|
2158
2207
|
};
|
|
2159
2208
|
};
|
|
2209
|
+
const initializePostHogExporter = () => {
|
|
2210
|
+
const posthogApiKey = getEffectiveEnvironmentVariable("POSTHOG_API_KEY");
|
|
2211
|
+
const posthogHost = getEffectiveEnvironmentVariable("POSTHOG_HOST");
|
|
2212
|
+
if (posthogApiKey) {
|
|
2213
|
+
logger.info("Initializing PostHog Trace Exporter...");
|
|
2214
|
+
return new otel.PostHogTraceExporter({
|
|
2215
|
+
apiKey: posthogApiKey.value,
|
|
2216
|
+
host: posthogHost?.value || "https://us.i.posthog.com"
|
|
2217
|
+
});
|
|
2218
|
+
}
|
|
2219
|
+
return void 0;
|
|
2220
|
+
};
|
|
2221
|
+
const getPostHogAiderEnvironmentVariables = (baseDir, settings) => {
|
|
2222
|
+
return {
|
|
2223
|
+
POSTHOG_API_KEY: getEffectiveEnvironmentVariable("POSTHOG_API_KEY", settings, baseDir)?.value,
|
|
2224
|
+
POSTHOG_API_URL: getEffectiveEnvironmentVariable("POSTHOG_HOST", settings, baseDir)?.value
|
|
2225
|
+
};
|
|
2226
|
+
};
|
|
2160
2227
|
const readEnvFile = (filePath) => {
|
|
2161
2228
|
try {
|
|
2162
2229
|
if (fs$1.existsSync(filePath)) {
|
|
@@ -2205,7 +2272,8 @@ const readApiKeyFromConfFile = (filePath, envVarName) => {
|
|
|
2205
2272
|
OPENCODE_API_KEY: ["opencode"],
|
|
2206
2273
|
REQUESTY_API_KEY: ["requesty"],
|
|
2207
2274
|
SYNTHETIC_API_KEY: ["synthetic"],
|
|
2208
|
-
MISTRAL_API_KEY: ["mistral"]
|
|
2275
|
+
MISTRAL_API_KEY: ["mistral"],
|
|
2276
|
+
NEURALWATT_API_KEY: ["neuralwatt"]
|
|
2209
2277
|
};
|
|
2210
2278
|
const providerNames = envVarToProviderName[envVarName] || [envVarName.replace(/_API_KEY$/, "").toLowerCase()];
|
|
2211
2279
|
const apiKeys = Array.isArray(apiKeyValue) ? apiKeyValue : [apiKeyValue];
|
|
@@ -2353,7 +2421,8 @@ const getEnvironmentVariablesForAider = (settings, baseDir) => {
|
|
|
2353
2421
|
};
|
|
2354
2422
|
const getTelemetryEnvironmentVariablesForAider = (settings, baseDir) => {
|
|
2355
2423
|
return {
|
|
2356
|
-
...getLangfuseEnvironmentVariables(baseDir, settings)
|
|
2424
|
+
...getLangfuseEnvironmentVariables(baseDir, settings),
|
|
2425
|
+
...getPostHogAiderEnvironmentVariables(baseDir, settings)
|
|
2357
2426
|
};
|
|
2358
2427
|
};
|
|
2359
2428
|
const getDefaultProjectSettings = (store, providerModels, baseDir, defaultAgentProfileId = DEFAULT_AGENT_PROFILE.id, providers) => {
|
|
@@ -2370,7 +2439,7 @@ const getDefaultProjectSettings = (store, providerModels, baseDir, defaultAgentP
|
|
|
2370
2439
|
modelEditFormats: {},
|
|
2371
2440
|
currentMode: "agent",
|
|
2372
2441
|
agentProfileId: defaultAgentProfileId,
|
|
2373
|
-
|
|
2442
|
+
autonomyModeLocked: false,
|
|
2374
2443
|
updatedFilesGroupMode: "flat",
|
|
2375
2444
|
disabledRuleFiles: [],
|
|
2376
2445
|
contextSidebarSectionsOrder: [],
|
|
@@ -2843,8 +2912,8 @@ const downloadUv = async () => {
|
|
|
2843
2912
|
strip: 1
|
|
2844
2913
|
});
|
|
2845
2914
|
} else if (filename.endsWith(".zip")) {
|
|
2846
|
-
const
|
|
2847
|
-
const zip = new
|
|
2915
|
+
const AdmZip2 = (await import("adm-zip")).default;
|
|
2916
|
+
const zip = new AdmZip2(tempFile);
|
|
2848
2917
|
zip.extractAllTo(AIDER_DESK_BIN_DIR, true);
|
|
2849
2918
|
}
|
|
2850
2919
|
if (platform !== "win32") {
|
|
@@ -2907,6 +2976,111 @@ const getLatestPythonLibVersion = async (library) => {
|
|
|
2907
2976
|
return null;
|
|
2908
2977
|
}
|
|
2909
2978
|
};
|
|
2979
|
+
const RIPGREP_VERSION = "15.1.0";
|
|
2980
|
+
const RIPGREP_BASE_URL = `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}`;
|
|
2981
|
+
const getRipgrepAssetName = () => {
|
|
2982
|
+
const platform = process.platform;
|
|
2983
|
+
const arch = process.arch;
|
|
2984
|
+
if (platform === "win32") {
|
|
2985
|
+
return arch === "arm64" ? `ripgrep-${RIPGREP_VERSION}-aarch64-pc-windows-msvc.zip` : `ripgrep-${RIPGREP_VERSION}-x86_64-pc-windows-msvc.zip`;
|
|
2986
|
+
}
|
|
2987
|
+
if (platform === "darwin") {
|
|
2988
|
+
return arch === "arm64" ? `ripgrep-${RIPGREP_VERSION}-aarch64-apple-darwin.tar.gz` : `ripgrep-${RIPGREP_VERSION}-x86_64-apple-darwin.tar.gz`;
|
|
2989
|
+
}
|
|
2990
|
+
if (arch === "arm64") {
|
|
2991
|
+
return `ripgrep-${RIPGREP_VERSION}-aarch64-unknown-linux-gnu.tar.gz`;
|
|
2992
|
+
}
|
|
2993
|
+
return `ripgrep-${RIPGREP_VERSION}-x86_64-unknown-linux-musl.tar.gz`;
|
|
2994
|
+
};
|
|
2995
|
+
const isRipgrepAvailable = () => {
|
|
2996
|
+
try {
|
|
2997
|
+
if (!fs$1.existsSync(RIPGREP_BINARY_PATH)) {
|
|
2998
|
+
return false;
|
|
2999
|
+
}
|
|
3000
|
+
const result = child_process.execSync(`"${RIPGREP_BINARY_PATH}" --version`, {
|
|
3001
|
+
encoding: "utf8",
|
|
3002
|
+
timeout: 5e3,
|
|
3003
|
+
windowsHide: true
|
|
3004
|
+
});
|
|
3005
|
+
if (!result.includes("ripgrep")) {
|
|
3006
|
+
return false;
|
|
3007
|
+
}
|
|
3008
|
+
logger.debug(`ripgrep is available at ${RIPGREP_BINARY_PATH}: ${result.trim()}`);
|
|
3009
|
+
return true;
|
|
3010
|
+
} catch {
|
|
3011
|
+
return false;
|
|
3012
|
+
}
|
|
3013
|
+
};
|
|
3014
|
+
const downloadRipgrep = async () => {
|
|
3015
|
+
const filename = getRipgrepAssetName();
|
|
3016
|
+
const url = `${RIPGREP_BASE_URL}/${filename}`;
|
|
3017
|
+
if (!fs$1.existsSync(AIDER_DESK_BIN_DIR)) {
|
|
3018
|
+
fs$1.mkdirSync(AIDER_DESK_BIN_DIR, { recursive: true });
|
|
3019
|
+
}
|
|
3020
|
+
const tempFile = path.join(AIDER_DESK_BIN_DIR, filename);
|
|
3021
|
+
const rgExeName = process.platform === "win32" ? "rg.exe" : "rg";
|
|
3022
|
+
try {
|
|
3023
|
+
logger.info(`Downloading ripgrep from ${url}`);
|
|
3024
|
+
const response = await fetch(url);
|
|
3025
|
+
if (!response.ok) {
|
|
3026
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
3027
|
+
}
|
|
3028
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
3029
|
+
fs$1.writeFileSync(tempFile, buffer);
|
|
3030
|
+
if (filename.endsWith(".tar.gz")) {
|
|
3031
|
+
const { extract } = await import("tar");
|
|
3032
|
+
await extract({
|
|
3033
|
+
cwd: AIDER_DESK_BIN_DIR,
|
|
3034
|
+
file: tempFile,
|
|
3035
|
+
strip: 1
|
|
3036
|
+
});
|
|
3037
|
+
} else if (filename.endsWith(".zip")) {
|
|
3038
|
+
const zip = new AdmZip(tempFile);
|
|
3039
|
+
const entry = zip.getEntry(rgExeName) || zip.getEntries().find((e) => e.entryName.endsWith(rgExeName));
|
|
3040
|
+
if (entry) {
|
|
3041
|
+
fs$1.writeFileSync(path.join(AIDER_DESK_BIN_DIR, rgExeName), entry.getData());
|
|
3042
|
+
} else {
|
|
3043
|
+
throw new Error(`Could not find ${rgExeName} in zip archive`);
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
3046
|
+
if (process.platform !== "win32") {
|
|
3047
|
+
fs$1.chmodSync(path.join(AIDER_DESK_BIN_DIR, rgExeName), 493);
|
|
3048
|
+
}
|
|
3049
|
+
logger.info(`ripgrep downloaded successfully to ${AIDER_DESK_BIN_DIR}`);
|
|
3050
|
+
} finally {
|
|
3051
|
+
if (fs$1.existsSync(tempFile)) {
|
|
3052
|
+
fs$1.unlinkSync(tempFile);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
};
|
|
3056
|
+
let ripgrepEnsured = false;
|
|
3057
|
+
let ripgrepEnsurePromise = null;
|
|
3058
|
+
const ensureRipgrepBinary = async () => {
|
|
3059
|
+
if (ripgrepEnsured) {
|
|
3060
|
+
return true;
|
|
3061
|
+
}
|
|
3062
|
+
if (!ripgrepEnsurePromise) {
|
|
3063
|
+
ripgrepEnsurePromise = (async () => {
|
|
3064
|
+
if (isRipgrepAvailable()) {
|
|
3065
|
+
ripgrepEnsured = true;
|
|
3066
|
+
return;
|
|
3067
|
+
}
|
|
3068
|
+
await downloadRipgrep();
|
|
3069
|
+
if (!isRipgrepAvailable()) {
|
|
3070
|
+
throw new Error("ripgrep binary not found after download");
|
|
3071
|
+
}
|
|
3072
|
+
ripgrepEnsured = true;
|
|
3073
|
+
})();
|
|
3074
|
+
}
|
|
3075
|
+
try {
|
|
3076
|
+
await ripgrepEnsurePromise;
|
|
3077
|
+
return true;
|
|
3078
|
+
} catch (error) {
|
|
3079
|
+
ripgrepEnsurePromise = null;
|
|
3080
|
+
logger.error("Failed to ensure ripgrep binary:", error);
|
|
3081
|
+
return false;
|
|
3082
|
+
}
|
|
3083
|
+
};
|
|
2910
3084
|
const isAbortError = (error) => {
|
|
2911
3085
|
if (!(error instanceof Error)) {
|
|
2912
3086
|
return false;
|
|
@@ -3078,18 +3252,25 @@ const expandTilde = (filePath) => {
|
|
|
3078
3252
|
}
|
|
3079
3253
|
return filePath;
|
|
3080
3254
|
};
|
|
3081
|
-
const readFileContent = async (absolutePath, withLines = false, lineOffset = 0, lineLimit = 1e3, sizeLimit = 0.05 * lineLimit) => {
|
|
3255
|
+
const readFileContent = async (absolutePath, withLines = false, lineOffset = 0, lineLimit = 1e3, sizeLimit = Math.max(50, 0.05 * lineLimit)) => {
|
|
3082
3256
|
const fileContentBuffer = await fs.readFile(absolutePath);
|
|
3083
3257
|
if (istextorbinary.isBinary(absolutePath, fileContentBuffer)) {
|
|
3084
3258
|
throw new Error("Binary files cannot be read.");
|
|
3085
3259
|
}
|
|
3086
|
-
const
|
|
3087
|
-
|
|
3088
|
-
|
|
3260
|
+
const fileContent = fileContentBuffer.toString("utf8");
|
|
3261
|
+
const lines = fileContent.split("\n");
|
|
3262
|
+
const totalLines = lines.length;
|
|
3263
|
+
const startIndex = Math.max(0, lineOffset);
|
|
3264
|
+
const endIndex = Math.min(totalLines, startIndex + lineLimit);
|
|
3265
|
+
const limitedLines = lines.slice(startIndex, endIndex);
|
|
3266
|
+
const limitedContent = limitedLines.join("\n");
|
|
3267
|
+
const limitedSizeKB = Buffer.byteLength(limitedContent, "utf8") / 1024;
|
|
3268
|
+
if (limitedSizeKB > sizeLimit) {
|
|
3269
|
+
const truncatedBytes = Buffer.from(limitedContent, "utf8").subarray(0, Math.floor(sizeLimit * 1024));
|
|
3089
3270
|
const truncatedContent = truncatedBytes.toString("utf8");
|
|
3090
3271
|
const truncatedLines = truncatedContent.split("\n");
|
|
3091
3272
|
if (withLines) {
|
|
3092
|
-
return truncatedLines.map((line, index) => `${index + 1}|${line}`).join("\n") + `
|
|
3273
|
+
return truncatedLines.map((line, index) => `${startIndex + index + 1}|${line}`).join("\n") + `
|
|
3093
3274
|
|
|
3094
3275
|
File size limit (${sizeLimit.toFixed(1)} KB) exceeded. Use shell commands (e.g., head, tail, grep) to read specific parts.`;
|
|
3095
3276
|
}
|
|
@@ -3097,33 +3278,30 @@ File size limit (${sizeLimit.toFixed(1)} KB) exceeded. Use shell commands (e.g.,
|
|
|
3097
3278
|
|
|
3098
3279
|
File size limit (${sizeLimit.toFixed(1)} KB) exceeded. Use shell commands (e.g., head, tail, grep) to read specific parts.`;
|
|
3099
3280
|
}
|
|
3100
|
-
|
|
3101
|
-
const lines = fileContent.split("\n");
|
|
3102
|
-
const totalLines = lines.length;
|
|
3103
|
-
const startIndex = Math.max(0, lineOffset);
|
|
3104
|
-
const endIndex = Math.min(totalLines, startIndex + lineLimit);
|
|
3105
|
-
let limitedLines = lines.slice(startIndex, endIndex);
|
|
3281
|
+
let resultLines = limitedLines;
|
|
3106
3282
|
if (withLines) {
|
|
3107
|
-
|
|
3283
|
+
resultLines = limitedLines.map((line, index) => `${startIndex + index + 1}|${line}`);
|
|
3108
3284
|
}
|
|
3109
3285
|
if (endIndex < totalLines) {
|
|
3110
|
-
|
|
3111
|
-
limitedLines.push(`Total lines in the file: ${totalLines}`);
|
|
3286
|
+
resultLines = [...resultLines, "...", `Total lines in the file: ${totalLines}`];
|
|
3112
3287
|
}
|
|
3113
|
-
return
|
|
3288
|
+
return resultLines.join("\n");
|
|
3114
3289
|
};
|
|
3115
|
-
const truncateToolResult = async (content, maxLines = 1e3, maxSizeKB = 50) => {
|
|
3290
|
+
const truncateToolResult = async (content, maxLines = 1e3, maxSizeKB = 50, maxTokens = 5e4, saveToFile = true, truncationSuffix) => {
|
|
3116
3291
|
const lines = content.split("\n");
|
|
3117
3292
|
const sizeBytes = Buffer.byteLength(content, "utf8");
|
|
3118
3293
|
const sizeKB = sizeBytes / 1024;
|
|
3119
|
-
|
|
3294
|
+
const tokenCount = maxTokens === Infinity ? 0 : gpt4o.encode(content).length;
|
|
3295
|
+
if (lines.length <= maxLines && sizeKB <= maxSizeKB && tokenCount <= maxTokens) {
|
|
3120
3296
|
return content;
|
|
3121
3297
|
}
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3298
|
+
let tmpFilePath;
|
|
3299
|
+
if (saveToFile) {
|
|
3300
|
+
const id = Date.now().toString(36) + Math.random().toString(36).substring(2, 8);
|
|
3301
|
+
const tmpFileName = `aider-desk-tool-result-${id}.txt`;
|
|
3302
|
+
tmpFilePath = path.join(os.tmpdir(), tmpFileName);
|
|
3303
|
+
await fs.writeFile(tmpFilePath, content, "utf8");
|
|
3304
|
+
}
|
|
3127
3305
|
const reasons = [];
|
|
3128
3306
|
if (lines.length > maxLines) {
|
|
3129
3307
|
reasons.push(`${lines.length} lines exceeded limit of ${maxLines}`);
|
|
@@ -3131,9 +3309,141 @@ const truncateToolResult = async (content, maxLines = 1e3, maxSizeKB = 50) => {
|
|
|
3131
3309
|
if (sizeKB > maxSizeKB) {
|
|
3132
3310
|
reasons.push(`${sizeKB.toFixed(1)} KB exceeded limit of ${maxSizeKB} KB`);
|
|
3133
3311
|
}
|
|
3134
|
-
|
|
3135
|
-
|
|
3312
|
+
if (tokenCount > maxTokens) {
|
|
3313
|
+
reasons.push(`${tokenCount} tokens exceeded limit of ${maxTokens}`);
|
|
3314
|
+
}
|
|
3315
|
+
const getSuffix = () => {
|
|
3316
|
+
if (truncationSuffix) {
|
|
3317
|
+
return truncationSuffix;
|
|
3318
|
+
}
|
|
3319
|
+
const fileNote = tmpFilePath ? ` Full content saved to ${tmpFilePath}.` : "";
|
|
3320
|
+
return `Content truncated (${reasons.join(", ")}).${fileNote}`;
|
|
3321
|
+
};
|
|
3322
|
+
if (tokenCount > maxTokens) {
|
|
3323
|
+
const headBudget = Math.floor(maxTokens / 2);
|
|
3324
|
+
const tailBudget = maxTokens - headBudget;
|
|
3325
|
+
const headLines = [];
|
|
3326
|
+
let headTokens = 0;
|
|
3327
|
+
for (const line of lines) {
|
|
3328
|
+
const lineTokens = gpt4o.encode(line).length;
|
|
3329
|
+
if (headTokens + lineTokens > headBudget) {
|
|
3330
|
+
break;
|
|
3331
|
+
}
|
|
3332
|
+
headLines.push(line);
|
|
3333
|
+
headTokens += lineTokens;
|
|
3334
|
+
}
|
|
3335
|
+
const tailLines = [];
|
|
3336
|
+
let tailTokens = 0;
|
|
3337
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3338
|
+
if (headLines.length + tailLines.length >= lines.length) {
|
|
3339
|
+
break;
|
|
3340
|
+
}
|
|
3341
|
+
const lineTokens = gpt4o.encode(lines[i]).length;
|
|
3342
|
+
if (tailTokens + lineTokens > tailBudget) {
|
|
3343
|
+
break;
|
|
3344
|
+
}
|
|
3345
|
+
tailLines.unshift(lines[i]);
|
|
3346
|
+
tailTokens += lineTokens;
|
|
3347
|
+
}
|
|
3348
|
+
const omittedLines = lines.length - headLines.length - tailLines.length;
|
|
3349
|
+
const truncationNotice = `
|
|
3350
|
+
|
|
3351
|
+
... ${omittedLines} lines omitted (${reasons.join(", ")}). Full content saved to ${tmpFilePath}.
|
|
3352
|
+
|
|
3353
|
+
`;
|
|
3354
|
+
if (truncationSuffix) {
|
|
3355
|
+
const suffixNotice = `
|
|
3356
|
+
|
|
3357
|
+
... ${omittedLines} lines omitted. ${truncationSuffix}
|
|
3358
|
+
|
|
3359
|
+
`;
|
|
3360
|
+
return headLines.join("\n") + suffixNotice + tailLines.join("\n");
|
|
3361
|
+
}
|
|
3362
|
+
return headLines.join("\n") + truncationNotice + tailLines.join("\n");
|
|
3363
|
+
}
|
|
3364
|
+
let preview;
|
|
3365
|
+
if (sizeKB > maxSizeKB) {
|
|
3366
|
+
const maxBytes = Math.floor(maxSizeKB * 1024);
|
|
3367
|
+
const contentBuffer = Buffer.from(content, "utf8");
|
|
3368
|
+
preview = contentBuffer.subarray(0, maxBytes).toString("utf8");
|
|
3369
|
+
} else {
|
|
3370
|
+
preview = lines.slice(0, maxLines).join("\n");
|
|
3371
|
+
}
|
|
3372
|
+
return preview + `
|
|
3373
|
+
... ${getSuffix()}`;
|
|
3374
|
+
};
|
|
3375
|
+
const NETWORK_ERROR_CODES = ["ECONNRESET", "EPIPE", "ETIMEDOUT", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH", "EAI_AGAIN"];
|
|
3376
|
+
const UNDICI_ERROR_PREFIX = "UND_ERR_";
|
|
3377
|
+
const isNetworkError = (error) => {
|
|
3378
|
+
if (!(error instanceof Error)) {
|
|
3379
|
+
return false;
|
|
3380
|
+
}
|
|
3381
|
+
if (error instanceof TypeError && error.message === "terminated") {
|
|
3382
|
+
return true;
|
|
3383
|
+
}
|
|
3384
|
+
if ("code" in error) {
|
|
3385
|
+
const code = error.code;
|
|
3386
|
+
if (typeof code === "string") {
|
|
3387
|
+
if (code.startsWith(UNDICI_ERROR_PREFIX)) {
|
|
3388
|
+
return true;
|
|
3389
|
+
}
|
|
3390
|
+
if (NETWORK_ERROR_CODES.includes(code)) {
|
|
3391
|
+
return true;
|
|
3392
|
+
}
|
|
3393
|
+
}
|
|
3394
|
+
}
|
|
3395
|
+
if (error.cause instanceof Error && isNetworkError(error.cause)) {
|
|
3396
|
+
return true;
|
|
3397
|
+
}
|
|
3398
|
+
return false;
|
|
3399
|
+
};
|
|
3400
|
+
const IMAGE_TOKEN_ESTIMATE = 1e3;
|
|
3401
|
+
const extractToolResultText = (output) => {
|
|
3402
|
+
if (!output || typeof output !== "object") {
|
|
3403
|
+
return "";
|
|
3404
|
+
}
|
|
3405
|
+
const o = output;
|
|
3406
|
+
if (o.type === "text" || o.type === "error-text") {
|
|
3407
|
+
return String(o.value ?? "");
|
|
3408
|
+
}
|
|
3409
|
+
if (o.type === "json" || o.type === "error-json") {
|
|
3410
|
+
return JSON.stringify(o.value);
|
|
3411
|
+
}
|
|
3412
|
+
if (o.type === "content" && Array.isArray(o.value)) {
|
|
3413
|
+
return o.value.map((v) => v.type === "text" ? v.text ?? "" : v.type === "media" ? "[media]" : "").join("\n");
|
|
3414
|
+
}
|
|
3415
|
+
return JSON.stringify(output);
|
|
3416
|
+
};
|
|
3417
|
+
const estimateMessageTokens = (messages) => {
|
|
3418
|
+
let estimatedImageTokens = 0;
|
|
3419
|
+
const textOnlyMessages = messages.map((msg) => {
|
|
3420
|
+
if (typeof msg.content === "string") {
|
|
3421
|
+
return msg;
|
|
3422
|
+
}
|
|
3423
|
+
if (!Array.isArray(msg.content)) {
|
|
3424
|
+
return { role: msg.role, content: "" };
|
|
3425
|
+
}
|
|
3426
|
+
const parts = msg.content;
|
|
3427
|
+
const textParts = [];
|
|
3428
|
+
for (const part of parts) {
|
|
3429
|
+
const type = part.type;
|
|
3430
|
+
if (type === "text" && typeof part.text === "string") {
|
|
3431
|
+
textParts.push(part.text);
|
|
3432
|
+
} else if (type === "tool-call") {
|
|
3433
|
+
textParts.push(JSON.stringify(part.input ?? ""));
|
|
3434
|
+
} else if (type === "tool-result") {
|
|
3435
|
+
textParts.push(extractToolResultText(part.output));
|
|
3436
|
+
} else if (type === "reasoning" && typeof part.text === "string") {
|
|
3437
|
+
textParts.push(part.text);
|
|
3438
|
+
} else if (type === "image") {
|
|
3439
|
+
estimatedImageTokens += IMAGE_TOKEN_ESTIMATE;
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3442
|
+
return { role: msg.role, content: textParts.join("\n\n") };
|
|
3443
|
+
});
|
|
3444
|
+
return textOnlyMessages.reduce((sum, msg) => sum + gpt4o.encode(typeof msg.content === "string" ? msg.content : "").length, 0) + estimatedImageTokens;
|
|
3136
3445
|
};
|
|
3446
|
+
const execFileAsync = util.promisify(child_process.execFile);
|
|
3137
3447
|
const fileLocks = /* @__PURE__ */ new Map();
|
|
3138
3448
|
const withFileLock = (filePath, operation) => {
|
|
3139
3449
|
logger.debug("Acquiring file lock:", { filePath });
|
|
@@ -3445,50 +3755,97 @@ Do not use escape characters \\ in the string like \\n or \\" and others. Do not
|
|
|
3445
3755
|
if (!isApproved) {
|
|
3446
3756
|
return `Grep search for '${searchTerm}' in files matching '${filePattern}' denied by user. Reason: ${userInput}`;
|
|
3447
3757
|
}
|
|
3758
|
+
const rgAvailable = await ensureRipgrepBinary();
|
|
3759
|
+
if (!rgAvailable) {
|
|
3760
|
+
return "Error: ripgrep binary is not available. Please try again or check the logs for details.";
|
|
3761
|
+
}
|
|
3448
3762
|
try {
|
|
3449
|
-
const
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3763
|
+
const rgArgs = ["--no-heading", "--line-number", "--color", "never", "--max-count", String(maxResults)];
|
|
3764
|
+
if (!caseSensitive) {
|
|
3765
|
+
rgArgs.push("-i");
|
|
3766
|
+
}
|
|
3767
|
+
if (contextLines > 0) {
|
|
3768
|
+
rgArgs.push("-C", String(contextLines));
|
|
3769
|
+
}
|
|
3770
|
+
rgArgs.push("-g", filePattern);
|
|
3771
|
+
rgArgs.push("--", searchTerm, ".");
|
|
3772
|
+
const taskDir = task.getTaskDir();
|
|
3773
|
+
logger.debug("Executing ripgrep:", {
|
|
3774
|
+
binary: RIPGREP_BINARY_PATH,
|
|
3775
|
+
args: rgArgs,
|
|
3776
|
+
cwd: taskDir
|
|
3454
3777
|
});
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3778
|
+
const { stdout, stderr } = await execFileAsync(RIPGREP_BINARY_PATH, rgArgs, {
|
|
3779
|
+
cwd: taskDir,
|
|
3780
|
+
env: { ...process.env, TERM: "dumb", PATH: getShellPath() },
|
|
3781
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
3782
|
+
windowsHide: true
|
|
3783
|
+
});
|
|
3784
|
+
logger.debug("ripgrep stdout raw:", { stdout: stdout.substring(0, 2e3) });
|
|
3785
|
+
logger.debug("ripgrep stderr:", { stderr: stderr.substring(0, 2e3) });
|
|
3786
|
+
const outputLines = stdout.split("\n").filter(Boolean);
|
|
3787
|
+
logger.debug("outputLines count:", { count: outputLines.length, lines: outputLines.slice(0, 20) });
|
|
3788
|
+
if (outputLines.length === 0) {
|
|
3789
|
+
return `No matches found for pattern '${searchTerm}' in files matching '${filePattern}'.`;
|
|
3461
3790
|
}
|
|
3462
3791
|
const results = [];
|
|
3463
|
-
const
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3792
|
+
const outputLineRegex = /^(.+?)([:-])(\d+)\2(.*)$/;
|
|
3793
|
+
let pendingBeforeContext = [];
|
|
3794
|
+
let afterContextBreak = false;
|
|
3795
|
+
for (let rawLine of outputLines) {
|
|
3796
|
+
if (rawLine.endsWith("\r")) {
|
|
3797
|
+
rawLine = rawLine.slice(0, -1);
|
|
3798
|
+
}
|
|
3799
|
+
if (rawLine === "--") {
|
|
3800
|
+
pendingBeforeContext = [];
|
|
3801
|
+
afterContextBreak = true;
|
|
3802
|
+
continue;
|
|
3803
|
+
}
|
|
3804
|
+
const line = rawLine;
|
|
3805
|
+
const match = outputLineRegex.exec(line);
|
|
3806
|
+
if (!match) {
|
|
3807
|
+
continue;
|
|
3808
|
+
}
|
|
3809
|
+
const [, rawFilePath, separator, lineNumStr, content] = match;
|
|
3810
|
+
const lineNum = parseInt(lineNumStr, 10);
|
|
3811
|
+
if (isNaN(lineNum)) {
|
|
3812
|
+
continue;
|
|
3813
|
+
}
|
|
3814
|
+
const filePath = rawFilePath.startsWith("./") ? rawFilePath.slice(2) : rawFilePath;
|
|
3815
|
+
const isMatch = separator === ":";
|
|
3816
|
+
if (isMatch) {
|
|
3817
|
+
const context = pendingBeforeContext.length > 0 ? [...pendingBeforeContext, content] : void 0;
|
|
3818
|
+
results.push({
|
|
3819
|
+
filePath,
|
|
3820
|
+
lineNumber: lineNum,
|
|
3821
|
+
lineContent: content,
|
|
3822
|
+
context
|
|
3823
|
+
});
|
|
3824
|
+
pendingBeforeContext = [];
|
|
3825
|
+
afterContextBreak = false;
|
|
3826
|
+
} else {
|
|
3827
|
+
if (afterContextBreak) {
|
|
3828
|
+
pendingBeforeContext.push(content);
|
|
3829
|
+
} else {
|
|
3830
|
+
const lastResult = results[results.length - 1];
|
|
3831
|
+
if (lastResult) {
|
|
3832
|
+
if (!lastResult.context) {
|
|
3833
|
+
lastResult.context = [];
|
|
3834
|
+
}
|
|
3835
|
+
lastResult.context.push(content);
|
|
3836
|
+
} else {
|
|
3837
|
+
pendingBeforeContext.push(content);
|
|
3486
3838
|
}
|
|
3487
|
-
results.push(matchResult);
|
|
3488
3839
|
}
|
|
3489
3840
|
}
|
|
3841
|
+
if (results.length >= maxResults) {
|
|
3842
|
+
break;
|
|
3843
|
+
}
|
|
3490
3844
|
}
|
|
3491
3845
|
if (results.length === 0) {
|
|
3846
|
+
if (stderr.trim()) {
|
|
3847
|
+
return `Error during grep: ${stderr.trim()}`;
|
|
3848
|
+
}
|
|
3492
3849
|
return `No matches found for pattern '${searchTerm}' in files matching '${filePattern}'.`;
|
|
3493
3850
|
}
|
|
3494
3851
|
const grouped = {};
|
|
@@ -3498,33 +3855,33 @@ Do not use escape characters \\ in the string like \\n or \\" and others. Do not
|
|
|
3498
3855
|
}
|
|
3499
3856
|
grouped[r.filePath].push(r);
|
|
3500
3857
|
}
|
|
3501
|
-
const
|
|
3502
|
-
|
|
3503
|
-
|
|
3858
|
+
const markdownLines = [];
|
|
3859
|
+
markdownLines.push(`## Grep Results: \`${searchTerm}\` in \`${filePattern}\` (${results.length} matches)`);
|
|
3860
|
+
markdownLines.push("");
|
|
3504
3861
|
for (const [filePath, matches] of Object.entries(grouped)) {
|
|
3505
|
-
|
|
3862
|
+
markdownLines.push(`### ${filePath} (${matches.length} ${matches.length === 1 ? "match" : "matches"})`);
|
|
3506
3863
|
for (const match of matches) {
|
|
3507
3864
|
const escapedContent = match.lineContent.replace(/`/g, "\\`");
|
|
3508
|
-
|
|
3865
|
+
markdownLines.push(`- **L${match.lineNumber}:** \`${escapedContent}\``);
|
|
3509
3866
|
if (match.context && match.context.length > 0) {
|
|
3510
|
-
|
|
3867
|
+
markdownLines.push(" ```");
|
|
3511
3868
|
for (const ctxLine of match.context) {
|
|
3512
|
-
|
|
3869
|
+
markdownLines.push(` ${ctxLine}`);
|
|
3513
3870
|
}
|
|
3514
|
-
|
|
3871
|
+
markdownLines.push(" ```");
|
|
3515
3872
|
}
|
|
3516
3873
|
}
|
|
3517
|
-
|
|
3874
|
+
markdownLines.push("");
|
|
3518
3875
|
}
|
|
3519
3876
|
const notices = [];
|
|
3520
3877
|
if (results.length >= maxResults) {
|
|
3521
3878
|
notices.push(`${maxResults} matches limit reached. Use maxResults=${maxResults * 2} for more, or refine pattern`);
|
|
3522
3879
|
}
|
|
3523
3880
|
if (notices.length > 0) {
|
|
3524
|
-
|
|
3525
|
-
|
|
3881
|
+
markdownLines.push("---");
|
|
3882
|
+
markdownLines.push(`[${notices.join(". ")}]`);
|
|
3526
3883
|
}
|
|
3527
|
-
return
|
|
3884
|
+
return await truncateToolResult(markdownLines.join("\n"), 1e3, 50, 1e4);
|
|
3528
3885
|
} catch (error) {
|
|
3529
3886
|
if (isAbortError(error)) {
|
|
3530
3887
|
return "Operation was cancelled by user.";
|
|
@@ -3581,6 +3938,18 @@ Timeout: ${timeout}ms`;
|
|
|
3581
3938
|
return `Bash command execution denied by user. Reason: ${userInput}`;
|
|
3582
3939
|
}
|
|
3583
3940
|
const absoluteCwd = expandedCwd ? path.resolve(task.getTaskDir(), expandedCwd) : task.getTaskDir();
|
|
3941
|
+
const isPosix = process.platform !== "win32";
|
|
3942
|
+
const killProcess = (pid) => {
|
|
3943
|
+
if (isPosix) {
|
|
3944
|
+
try {
|
|
3945
|
+
process.kill(-pid, "SIGKILL");
|
|
3946
|
+
} catch {
|
|
3947
|
+
treeKill(pid, "SIGKILL");
|
|
3948
|
+
}
|
|
3949
|
+
} else {
|
|
3950
|
+
treeKill(pid, "SIGKILL");
|
|
3951
|
+
}
|
|
3952
|
+
};
|
|
3584
3953
|
return await new Promise((resolve) => {
|
|
3585
3954
|
let stdout = "";
|
|
3586
3955
|
let stderr = "";
|
|
@@ -3604,8 +3973,8 @@ Timeout: ${timeout}ms`;
|
|
|
3604
3973
|
}
|
|
3605
3974
|
isResolved = true;
|
|
3606
3975
|
cleanup();
|
|
3607
|
-
const truncatedStdout = await truncateToolResult(stdout);
|
|
3608
|
-
const truncatedStderr = await truncateToolResult(stderr);
|
|
3976
|
+
const truncatedStdout = await truncateToolResult(stdout, 1e3, 50, 1e4);
|
|
3977
|
+
const truncatedStderr = await truncateToolResult(stderr, 1e3, 50, 1e4);
|
|
3609
3978
|
resolve({ stdout: truncatedStdout, stderr: truncatedStderr, exitCode });
|
|
3610
3979
|
};
|
|
3611
3980
|
abortListener = () => {
|
|
@@ -3613,7 +3982,7 @@ Timeout: ${timeout}ms`;
|
|
|
3613
3982
|
return;
|
|
3614
3983
|
}
|
|
3615
3984
|
if (childProcess?.pid) {
|
|
3616
|
-
|
|
3985
|
+
killProcess(childProcess.pid);
|
|
3617
3986
|
}
|
|
3618
3987
|
stderr = "Operation was cancelled by user.";
|
|
3619
3988
|
exitCode = 130;
|
|
@@ -3627,6 +3996,7 @@ Timeout: ${timeout}ms`;
|
|
|
3627
3996
|
env: { ...process.env, TERM: "dumb", DEBIAN_FRONTEND: "noninteractive", PATH: getShellPath() },
|
|
3628
3997
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3629
3998
|
// Explicitly pipe stdout and stderr to capture output from piped commands
|
|
3999
|
+
detached: isPosix,
|
|
3630
4000
|
signal: abortSignal
|
|
3631
4001
|
});
|
|
3632
4002
|
timeoutHandle = setTimeout(() => {
|
|
@@ -3634,7 +4004,7 @@ Timeout: ${timeout}ms`;
|
|
|
3634
4004
|
return;
|
|
3635
4005
|
}
|
|
3636
4006
|
if (childProcess?.pid) {
|
|
3637
|
-
|
|
4007
|
+
killProcess(childProcess.pid);
|
|
3638
4008
|
}
|
|
3639
4009
|
stderr = `Error: Command timed out after ${timeout}ms. Consider increasing the timeout parameter.`;
|
|
3640
4010
|
exitCode = 124;
|
|
@@ -3745,7 +4115,8 @@ Format: ${format}`;
|
|
|
3745
4115
|
return `Error: Invalid URL provided: ${url}. Please provide a valid URL.`;
|
|
3746
4116
|
}
|
|
3747
4117
|
try {
|
|
3748
|
-
|
|
4118
|
+
const content = await scrapeWeb(url, timeout, abortSignal, format);
|
|
4119
|
+
return await truncateToolResult(content, 1e3, 50, 1e4);
|
|
3749
4120
|
} catch (error) {
|
|
3750
4121
|
if (isAbortError(error)) {
|
|
3751
4122
|
return "Operation was cancelled by user.";
|
|
@@ -4014,6 +4385,11 @@ const createTasksToolset = (settings, task, profile, promptContext) => {
|
|
|
4014
4385
|
if (state) {
|
|
4015
4386
|
allTasks = allTasks.filter((t) => t.state === state);
|
|
4016
4387
|
}
|
|
4388
|
+
allTasks.sort((a, b) => {
|
|
4389
|
+
const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
|
|
4390
|
+
const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
|
|
4391
|
+
return dateB - dateA;
|
|
4392
|
+
});
|
|
4017
4393
|
const startIndex = Math.max(0, offset);
|
|
4018
4394
|
const endIndex = startIndex + Math.max(0, limit || allTasks.length);
|
|
4019
4395
|
const paginatedTasks = allTasks.slice(startIndex, endIndex);
|
|
@@ -4130,34 +4506,35 @@ const createTasksToolset = (settings, task, profile, promptContext) => {
|
|
|
4130
4506
|
}
|
|
4131
4507
|
}
|
|
4132
4508
|
});
|
|
4133
|
-
const isSubtask = task.task.parentId !== null;
|
|
4134
4509
|
const autoGenerateTaskName = settings.taskSettings.autoGenerateTaskName;
|
|
4135
4510
|
const nameProperty = autoGenerateTaskName ? zod.z.string().optional().describe("Optional concise name for the new task. If not provided, the task name will be auto-generated from the prompt.") : zod.z.string().describe("Concise name for the new task.");
|
|
4136
|
-
const
|
|
4137
|
-
|
|
4138
|
-
|
|
4139
|
-
|
|
4140
|
-
|
|
4141
|
-
mode: zod.z.string().optional().default("agent").describe("Optional mode to use for the task. Use only when explicitly requested by the user."),
|
|
4142
|
-
asSubtask: zod.z.boolean().optional().default(false).describe("If true, the task will be created as a subtask of the current task. Use only when explicitly requested by the user."),
|
|
4143
|
-
autoApprove: zod.z.boolean().optional().default(false).describe(
|
|
4144
|
-
"If true, the task will be created with auto-approve enabled. Set autoApprove to true when no planning is needed, just execution of the task with all the work."
|
|
4145
|
-
),
|
|
4146
|
-
worktree: zod.z.boolean().optional().default(false).describe("If true, the task will be created in worktree mode. Use only when explicitly requested by the user."),
|
|
4147
|
-
executeAndWait: zod.z.boolean().optional().default(false).describe("If true, the task will be created and executed immediately and the tool will wait for it to complete before returning."),
|
|
4148
|
-
executeInBackground: zod.z.boolean().optional().default(false).describe("If true, the task will be created and executed in the background.")
|
|
4149
|
-
});
|
|
4150
|
-
const CreateTaskWithParentInputSchema = CreateTaskInputSchema.extend({
|
|
4151
|
-
parentTaskId: zod.z.string().nullable().optional().describe(
|
|
4152
|
-
`Optional ID of the parent task. If provided, the new task will be created as a subtask of the specified parent. Use the current task's ID (${task.taskId}) to create a subtask of the current task. When asSubtask is true, this tasks ID will be used automatically.`
|
|
4153
|
-
)
|
|
4154
|
-
});
|
|
4511
|
+
const availableAgents = task.getProject().getAgentProfiles().map((p) => {
|
|
4512
|
+
const slug = deriveDirName(p.name, /* @__PURE__ */ new Set());
|
|
4513
|
+
const shortId = isUuid(p.id) ? p.id.substring(0, 8) : p.id;
|
|
4514
|
+
return `${slug}(${shortId})`;
|
|
4515
|
+
}).join(",");
|
|
4155
4516
|
const createTaskTool = ai.tool({
|
|
4156
4517
|
description: TASKS_TOOL_DESCRIPTIONS[TASKS_TOOL_CREATE_TASK],
|
|
4157
|
-
inputSchema:
|
|
4518
|
+
inputSchema: zod.z.object({
|
|
4519
|
+
prompt: zod.z.string().describe("The initial prompt for the new task"),
|
|
4520
|
+
name: nameProperty,
|
|
4521
|
+
agentProfileId: zod.z.string().optional().describe(`Optional agent profile ID or name. Available agents: ${availableAgents}. Use only when explicitly requested by the user.`),
|
|
4522
|
+
modelId: zod.z.string().optional().describe("Optional model ID in the format `provider/model` to use for the task. Use only when explicitly requested by the user."),
|
|
4523
|
+
mode: zod.z.string().optional().default("agent").describe("Optional mode to use for the task. Use only when explicitly requested by the user."),
|
|
4524
|
+
asSubtask: zod.z.boolean().optional().default(false).describe("If true, the task will be created as a subtask of the current task. Use only when explicitly requested by the user."),
|
|
4525
|
+
parentTaskId: zod.z.string().nullable().optional().describe(
|
|
4526
|
+
"Optional ID of the parent task. If provided, the new task will be created as a subtask of the specified parent. When asSubtask is specified as true, this is not needed as parent ID is resolved automatically."
|
|
4527
|
+
),
|
|
4528
|
+
autonomyMode: zod.z.enum(AutonomyMode).optional().describe(
|
|
4529
|
+
'The autonomy mode for the new task. "manual" requires approval for every tool and plan, "guided" (default) auto-approves tools but waits for plan approval from user, "autonomous" runs without interruption.'
|
|
4530
|
+
),
|
|
4531
|
+
worktree: zod.z.boolean().optional().default(false).describe("If true, the task will be created in worktree mode. Use only when explicitly requested by the user."),
|
|
4532
|
+
executeAndWait: zod.z.boolean().optional().default(false).describe("If true, the task will be created and executed immediately and the tool will wait for it to complete before returning."),
|
|
4533
|
+
executeInBackground: zod.z.boolean().optional().default(false).describe("If true, the task will be created and executed in the background.")
|
|
4534
|
+
}),
|
|
4158
4535
|
execute: async (input, { toolCallId }) => {
|
|
4159
|
-
const { prompt, name, agentProfileId, modelId, mode = "agent", asSubtask = false,
|
|
4160
|
-
const parentTaskId = asSubtask ? task.taskId : "parentTaskId" in input ? input.parentTaskId : void 0;
|
|
4536
|
+
const { prompt, name, agentProfileId, modelId, mode = "agent", asSubtask = false, autonomyMode, worktree, executeAndWait, executeInBackground } = input;
|
|
4537
|
+
const parentTaskId = asSubtask ? task.task.parentId || task.taskId : "parentTaskId" in input ? input.parentTaskId : void 0;
|
|
4161
4538
|
task.addToolMessage(toolCallId, TASKS_TOOL_GROUP_NAME, TASKS_TOOL_CREATE_TASK, input, void 0, void 0, promptContext);
|
|
4162
4539
|
const toolName = `${TASKS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${TASKS_TOOL_CREATE_TASK}`;
|
|
4163
4540
|
const questionKey = toolName;
|
|
@@ -4174,18 +4551,27 @@ Parent Task ID: ${parentTaskId || "none (top-level task)"}` : ""}`;
|
|
|
4174
4551
|
const newTask = await task.getProject().createNewTask({
|
|
4175
4552
|
parentId: parentTaskId || null,
|
|
4176
4553
|
name: name || "",
|
|
4177
|
-
|
|
4554
|
+
autonomyMode,
|
|
4178
4555
|
workingMode: worktree ? "worktree" : "local"
|
|
4179
4556
|
});
|
|
4180
4557
|
const updates = {};
|
|
4181
4558
|
if (agentProfileId) {
|
|
4182
|
-
|
|
4559
|
+
const resolvedProfile = task.getProject().resolveAgentProfile(agentProfileId);
|
|
4560
|
+
if (resolvedProfile) {
|
|
4561
|
+
updates.agentProfileId = resolvedProfile.id;
|
|
4562
|
+
} else {
|
|
4563
|
+
const availableProfiles = task.getProject().getAgentProfiles().map((p) => `"${p.name}"`).join(", ");
|
|
4564
|
+
return `Agent profile '${agentProfileId}' not found. Available profiles: ${availableProfiles}`;
|
|
4565
|
+
}
|
|
4183
4566
|
}
|
|
4184
4567
|
if (modelId) {
|
|
4185
4568
|
const [provider, ...modelParts] = modelId.split("/");
|
|
4186
4569
|
updates.provider = provider;
|
|
4187
4570
|
updates.model = modelParts.join("/");
|
|
4188
4571
|
updates.mainModel = modelId;
|
|
4572
|
+
} else {
|
|
4573
|
+
updates.provider = void 0;
|
|
4574
|
+
updates.model = void 0;
|
|
4189
4575
|
}
|
|
4190
4576
|
const taskInstance = task.getProject().getTask(newTask.id);
|
|
4191
4577
|
if (!taskInstance) {
|
|
@@ -4243,6 +4629,66 @@ Parent Task ID: ${parentTaskId || "none (top-level task)"}` : ""}`;
|
|
|
4243
4629
|
}
|
|
4244
4630
|
}
|
|
4245
4631
|
});
|
|
4632
|
+
const runPromptTool = ai.tool({
|
|
4633
|
+
description: TASKS_TOOL_DESCRIPTIONS[TASKS_TOOL_RUN_PROMPT],
|
|
4634
|
+
inputSchema: zod.z.object({
|
|
4635
|
+
taskId: zod.z.string().describe("The ID of the existing task to run the prompt on"),
|
|
4636
|
+
prompt: zod.z.string().describe("The prompt to run on the task"),
|
|
4637
|
+
mode: zod.z.string().optional().describe("Optional mode to use for the prompt. Use only when explicitly requested by the user."),
|
|
4638
|
+
executeAndWait: zod.z.boolean().optional().default(true).describe("If true (default), the tool will wait for the prompt to complete before returning. If false, the prompt will run in the background.")
|
|
4639
|
+
}),
|
|
4640
|
+
execute: async (input, { toolCallId }) => {
|
|
4641
|
+
const { taskId, prompt, mode, executeAndWait = true } = input;
|
|
4642
|
+
task.addToolMessage(
|
|
4643
|
+
toolCallId,
|
|
4644
|
+
TASKS_TOOL_GROUP_NAME,
|
|
4645
|
+
TASKS_TOOL_RUN_PROMPT,
|
|
4646
|
+
{ taskId, prompt, mode, executeAndWait },
|
|
4647
|
+
void 0,
|
|
4648
|
+
void 0,
|
|
4649
|
+
promptContext
|
|
4650
|
+
);
|
|
4651
|
+
const toolName = `${TASKS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${TASKS_TOOL_RUN_PROMPT}`;
|
|
4652
|
+
const questionKey = toolName;
|
|
4653
|
+
const questionText = `Approve running prompt on task ${taskId}?`;
|
|
4654
|
+
const questionSubject = `Task ID: ${taskId}
|
|
4655
|
+
Prompt: ${prompt}${executeAndWait ? "\nWait for completion: true" : "\nRun in background: true"}`;
|
|
4656
|
+
const [isApproved, userInput] = await approvalManager.handleToolApproval(toolName, input, questionKey, questionText, questionSubject);
|
|
4657
|
+
if (!isApproved) {
|
|
4658
|
+
return `Running prompt on task denied by user. Reason: ${userInput}`;
|
|
4659
|
+
}
|
|
4660
|
+
try {
|
|
4661
|
+
const targetTask = task.getProject().getTask(taskId);
|
|
4662
|
+
if (!targetTask) {
|
|
4663
|
+
return `Task with ID ${taskId} not found`;
|
|
4664
|
+
}
|
|
4665
|
+
const effectiveMode = mode || targetTask.task.currentMode || "agent";
|
|
4666
|
+
const run = targetTask.runPrompt(prompt, effectiveMode, false);
|
|
4667
|
+
if (executeAndWait) {
|
|
4668
|
+
await run;
|
|
4669
|
+
}
|
|
4670
|
+
const taskState = targetTask.task.state;
|
|
4671
|
+
const contextMessages = await targetTask.getContextMessages();
|
|
4672
|
+
let resultMessage = executeAndWait ? "Prompt has been executed successfully" : "Prompt has been started in the background";
|
|
4673
|
+
if (executeAndWait && taskState === DefaultTaskState.Interrupted) {
|
|
4674
|
+
resultMessage = "Task has been interrupted";
|
|
4675
|
+
} else if (executeAndWait && taskState === DefaultTaskState.Delegated) {
|
|
4676
|
+
resultMessage = "Task has been delegated to a subagent";
|
|
4677
|
+
}
|
|
4678
|
+
return {
|
|
4679
|
+
taskId,
|
|
4680
|
+
result: resultMessage,
|
|
4681
|
+
state: taskState,
|
|
4682
|
+
...executeAndWait && contextMessages.length > 0 && {
|
|
4683
|
+
lastMessage: contextMessages[contextMessages.length - 1]
|
|
4684
|
+
}
|
|
4685
|
+
};
|
|
4686
|
+
} catch (error) {
|
|
4687
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4688
|
+
return `Error running prompt on task: ${errorMessage}`;
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
});
|
|
4246
4692
|
const searchTaskTool = ai.tool({
|
|
4247
4693
|
description: TASKS_TOOL_DESCRIPTIONS[TASKS_TOOL_SEARCH_TASK],
|
|
4248
4694
|
inputSchema: zod.z.object({
|
|
@@ -4309,6 +4755,7 @@ Please, consider reporting an issue at https://github.com/hotovo/aider-desk/issu
|
|
|
4309
4755
|
[`${TASKS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${TASKS_TOOL_GET_TASK_MESSAGE}`]: getTaskMessageTool,
|
|
4310
4756
|
[`${TASKS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${TASKS_TOOL_CREATE_TASK}`]: createTaskTool,
|
|
4311
4757
|
[`${TASKS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${TASKS_TOOL_DELETE_TASK}`]: deleteTaskTool,
|
|
4758
|
+
[`${TASKS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${TASKS_TOOL_RUN_PROMPT}`]: runPromptTool,
|
|
4312
4759
|
[`${TASKS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${TASKS_TOOL_SEARCH_TASK}`]: searchTaskTool
|
|
4313
4760
|
};
|
|
4314
4761
|
const filteredTools = {};
|
|
@@ -4542,7 +4989,7 @@ const createAiderToolset = (task, profile, promptContext) => {
|
|
|
4542
4989
|
}
|
|
4543
4990
|
task.addToolMessage(toolCallId, AIDER_TOOL_GROUP_NAME, AIDER_TOOL_RUN_PROMPT, { prompt }, void 0, void 0, aiderPromptContext);
|
|
4544
4991
|
const responses = await task.sendPromptToAider(prompt, aiderPromptContext, "code", [], void 0, {
|
|
4545
|
-
autoApprove: task.task.
|
|
4992
|
+
autoApprove: task.task.autonomyMode !== AutonomyMode.Manual,
|
|
4546
4993
|
denyCommands: true
|
|
4547
4994
|
});
|
|
4548
4995
|
task.addLogMessage("loading");
|
|
@@ -5748,7 +6195,8 @@ const addImportantReminders = async (task, profile, projectProfiles, userRequest
|
|
|
5748
6195
|
${subagents}`);
|
|
5749
6196
|
}
|
|
5750
6197
|
}
|
|
5751
|
-
|
|
6198
|
+
const shouldPresentPlan = task.task.autonomyMode !== AutonomyMode.Autonomous;
|
|
6199
|
+
if (shouldPresentPlan && !profile.isSubagent) {
|
|
5752
6200
|
reminders.push("Before making any complex changes, present the plan and wait for my approval.");
|
|
5753
6201
|
}
|
|
5754
6202
|
if (task.getTaskDir() !== task.getProjectDir()) {
|
|
@@ -5774,6 +6222,7 @@ ${reminder}
|
|
|
5774
6222
|
"onImportantReminders",
|
|
5775
6223
|
{
|
|
5776
6224
|
profile,
|
|
6225
|
+
agentProfile: profile,
|
|
5777
6226
|
remindersContent
|
|
5778
6227
|
},
|
|
5779
6228
|
task.project,
|
|
@@ -5791,9 +6240,12 @@ ${reminder}
|
|
|
5791
6240
|
${remindersContent}
|
|
5792
6241
|
</ThisIsImportant>`;
|
|
5793
6242
|
}
|
|
5794
|
-
const updatedFirstUserMessage = {
|
|
6243
|
+
const updatedFirstUserMessage = typeof userRequestMessage.content === "string" ? {
|
|
5795
6244
|
...userRequestMessage,
|
|
5796
6245
|
content: `${userRequestMessage.content}${remindersContent}`
|
|
6246
|
+
} : {
|
|
6247
|
+
...userRequestMessage,
|
|
6248
|
+
content: [...userRequestMessage.content, { type: "text", text: remindersContent }]
|
|
5797
6249
|
};
|
|
5798
6250
|
const newMessages = [...messages];
|
|
5799
6251
|
newMessages[userRequestMessageIndex] = updatedFirstUserMessage;
|
|
@@ -6237,9 +6689,9 @@ ${fileList}`
|
|
|
6237
6689
|
const extensionTools = this.extensionManager.createExtensionToolset(task, mode, profile, toolSet, abortSignal);
|
|
6238
6690
|
Object.assign(toolSet, extensionTools);
|
|
6239
6691
|
}
|
|
6240
|
-
return this.wrapToolsWithHooks(task, toolSet, abortSignal, promptContext);
|
|
6692
|
+
return this.wrapToolsWithHooks(task, profile, toolSet, abortSignal, promptContext);
|
|
6241
6693
|
}
|
|
6242
|
-
wrapToolsWithHooks(task, toolSet, abortSignal, promptContext) {
|
|
6694
|
+
wrapToolsWithHooks(task, profile, toolSet, abortSignal, promptContext) {
|
|
6243
6695
|
const wrappedToolSet = {};
|
|
6244
6696
|
for (const [toolName, toolDef] of Object.entries(toolSet)) {
|
|
6245
6697
|
wrappedToolSet[toolName] = {
|
|
@@ -6250,7 +6702,7 @@ ${fileList}`
|
|
|
6250
6702
|
task.addToolMessage(options.toolCallId, serverName, messageToolName, effectiveInput, void 0, void 0, promptContext);
|
|
6251
6703
|
const toolCalledExtensionResult = await this.extensionManager.dispatchEvent(
|
|
6252
6704
|
"onToolCalled",
|
|
6253
|
-
{ toolName, input: effectiveInput, abortSignal: options.abortSignal || abortSignal },
|
|
6705
|
+
{ toolName, agentProfile: profile, input: effectiveInput, abortSignal: options.abortSignal || abortSignal },
|
|
6254
6706
|
task.project,
|
|
6255
6707
|
task
|
|
6256
6708
|
);
|
|
@@ -6267,7 +6719,7 @@ ${fileList}`
|
|
|
6267
6719
|
}
|
|
6268
6720
|
const toolFinishedExtensionResult = await this.extensionManager.dispatchEvent(
|
|
6269
6721
|
"onToolFinished",
|
|
6270
|
-
{ toolName, input: effectiveInput, output: result },
|
|
6722
|
+
{ toolName, agentProfile: profile, input: effectiveInput, output: result },
|
|
6271
6723
|
task.project,
|
|
6272
6724
|
task
|
|
6273
6725
|
);
|
|
@@ -6423,7 +6875,7 @@ ${fileList}`
|
|
|
6423
6875
|
}
|
|
6424
6876
|
return inputSchema;
|
|
6425
6877
|
}
|
|
6426
|
-
async runAgent(task, profile, prompt, mode = "agent", promptContext, initialContextMessages, initialContextFiles, systemPrompt, includeInContext = true, abortSignal) {
|
|
6878
|
+
async runAgent(task, profile, prompt, mode = "agent", promptContext, initialContextMessages, initialContextFiles, systemPrompt, includeInContext = true, abortSignal, images) {
|
|
6427
6879
|
let contextMessages = initialContextMessages ?? await task.getContextMessages();
|
|
6428
6880
|
let contextFiles = initialContextFiles ?? await task.getContextFiles();
|
|
6429
6881
|
if (!systemPrompt) {
|
|
@@ -6432,9 +6884,33 @@ ${fileList}`
|
|
|
6432
6884
|
const userRequestMessage = prompt ? {
|
|
6433
6885
|
id: promptContext?.id || uuid.v4(),
|
|
6434
6886
|
role: "user",
|
|
6435
|
-
content:
|
|
6436
|
-
|
|
6887
|
+
content: images && images.length > 0 ? [
|
|
6888
|
+
{ type: "text", text: prompt },
|
|
6889
|
+
...images.map((dataUrl) => {
|
|
6890
|
+
const match = dataUrl.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
6891
|
+
return {
|
|
6892
|
+
type: "image",
|
|
6893
|
+
image: match ? match[2] : dataUrl,
|
|
6894
|
+
mediaType: match ? match[1] : void 0
|
|
6895
|
+
};
|
|
6896
|
+
})
|
|
6897
|
+
] : prompt,
|
|
6898
|
+
promptContext,
|
|
6899
|
+
timestamp: Date.now()
|
|
6437
6900
|
} : null;
|
|
6901
|
+
if (userRequestMessage) {
|
|
6902
|
+
logger.info("User request message created:", {
|
|
6903
|
+
id: userRequestMessage.id,
|
|
6904
|
+
contentType: typeof userRequestMessage.content === "string" ? "string" : "parts",
|
|
6905
|
+
...typeof userRequestMessage.content === "string" ? { contentPreview: userRequestMessage.content.substring(0, 200) } : {
|
|
6906
|
+
parts: userRequestMessage.content.map((part) => ({
|
|
6907
|
+
type: part.type,
|
|
6908
|
+
...part.type === "text" ? { textPreview: part.text?.substring(0, 100) } : {},
|
|
6909
|
+
...part.type === "image" ? { mediaType: part.mediaType, imagePreview: typeof part.image === "string" ? part.image.substring(0, 80) : typeof part.image } : {}
|
|
6910
|
+
}))
|
|
6911
|
+
}
|
|
6912
|
+
});
|
|
6913
|
+
}
|
|
6438
6914
|
const settings = this.store.getSettings();
|
|
6439
6915
|
const projectProfiles = this.agentProfileManager.getProjectProfiles(task.project);
|
|
6440
6916
|
let resultMessages = userRequestMessage ? [userRequestMessage] : [];
|
|
@@ -6653,7 +7129,7 @@ ${fileList}`
|
|
|
6653
7129
|
return null;
|
|
6654
7130
|
}
|
|
6655
7131
|
};
|
|
6656
|
-
const modelSettings = this.modelManager.getModelSettings(provider.
|
|
7132
|
+
const modelSettings = this.modelManager.getModelSettings(provider.id, modelName);
|
|
6657
7133
|
const effectiveTemperature = profile.temperature ?? modelSettings?.temperature;
|
|
6658
7134
|
const effectiveMaxOutputTokens = profile.maxTokens ?? modelSettings?.maxOutputTokens;
|
|
6659
7135
|
logger.debug("Parameters:", {
|
|
@@ -6711,9 +7187,9 @@ ${fileList}`
|
|
|
6711
7187
|
let iterationCount = 0;
|
|
6712
7188
|
let retryCount = 0;
|
|
6713
7189
|
while (true) {
|
|
6714
|
-
logger.info(`Starting iteration ${iterationCount}
|
|
7190
|
+
logger.info(`Starting iteration ${iterationCount}`, { task: task.taskId });
|
|
6715
7191
|
iterationCount++;
|
|
6716
|
-
if (iterationCount > profile.maxIterations) {
|
|
7192
|
+
if (profile.maxIterations > 0 && iterationCount > profile.maxIterations) {
|
|
6717
7193
|
logger.warn(`Max iterations (${profile.maxIterations}) reached. Stopping agent.`);
|
|
6718
7194
|
task.addLogMessage(
|
|
6719
7195
|
"warning",
|
|
@@ -6726,7 +7202,7 @@ ${fileList}`
|
|
|
6726
7202
|
let iterationError = null;
|
|
6727
7203
|
let hasReasoning = false;
|
|
6728
7204
|
let finishReason = null;
|
|
6729
|
-
let
|
|
7205
|
+
let currentStepMessages = [];
|
|
6730
7206
|
let responseMessageIndex = 0;
|
|
6731
7207
|
const onStepFinish = async (stepResult) => {
|
|
6732
7208
|
finishReason = stepResult.finishReason;
|
|
@@ -6738,7 +7214,7 @@ ${fileList}`
|
|
|
6738
7214
|
logger.info("Prompt aborted by user");
|
|
6739
7215
|
return;
|
|
6740
7216
|
}
|
|
6741
|
-
|
|
7217
|
+
currentStepMessages = await this.processStep(currentResponseId, stepResult, task, provider, modelName, promptContext, abortSignal);
|
|
6742
7218
|
const extensionResult2 = await this.extensionManager.dispatchEvent(
|
|
6743
7219
|
"onAgentStepFinished",
|
|
6744
7220
|
{
|
|
@@ -6747,18 +7223,18 @@ ${fileList}`
|
|
|
6747
7223
|
currentResponseId,
|
|
6748
7224
|
stepResult,
|
|
6749
7225
|
finishReason,
|
|
6750
|
-
responseMessages
|
|
7226
|
+
responseMessages: currentStepMessages
|
|
6751
7227
|
},
|
|
6752
7228
|
task.project,
|
|
6753
7229
|
task
|
|
6754
7230
|
);
|
|
6755
|
-
|
|
7231
|
+
currentStepMessages = extensionResult2.responseMessages;
|
|
6756
7232
|
finishReason = extensionResult2.finishReason;
|
|
6757
7233
|
currentResponseId = uuid.v4();
|
|
6758
7234
|
responseMessageIndex = 0;
|
|
6759
7235
|
hasReasoning = false;
|
|
6760
7236
|
streamingMessageIds.clear();
|
|
6761
|
-
if (
|
|
7237
|
+
if (currentStepMessages.length > 0) {
|
|
6762
7238
|
retryCount = 0;
|
|
6763
7239
|
}
|
|
6764
7240
|
};
|
|
@@ -6831,25 +7307,46 @@ ${fileList}`
|
|
|
6831
7307
|
onStepFinish,
|
|
6832
7308
|
experimental_repairToolCall: repairToolCall
|
|
6833
7309
|
});
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
if (
|
|
7310
|
+
try {
|
|
7311
|
+
for await (const chunk of result.fullStream) {
|
|
7312
|
+
logger.debug("Chunk:", { chunk: chunk.type, responseMessageIndex });
|
|
7313
|
+
const responseMessageId = responseMessageIndex > 0 ? `${currentResponseId}-${responseMessageIndex}` : currentResponseId;
|
|
7314
|
+
if (chunk.type === "text-start") {
|
|
7315
|
+
if (hasReasoning) {
|
|
7316
|
+
streamingMessageIds.add(responseMessageId);
|
|
7317
|
+
await task.processResponseMessage({
|
|
7318
|
+
id: responseMessageId,
|
|
7319
|
+
action: "response",
|
|
7320
|
+
content: ANSWER_RESPONSE_START_TAG,
|
|
7321
|
+
finished: false,
|
|
7322
|
+
promptContext
|
|
7323
|
+
});
|
|
7324
|
+
hasReasoning = false;
|
|
7325
|
+
}
|
|
7326
|
+
} else if (chunk.type === "text-end") {
|
|
7327
|
+
responseMessageIndex++;
|
|
7328
|
+
} else if (chunk.type === "text-delta") {
|
|
7329
|
+
if (chunk.text.trim()) {
|
|
7330
|
+
streamingMessageIds.add(responseMessageId);
|
|
7331
|
+
await task.processResponseMessage({
|
|
7332
|
+
id: responseMessageId,
|
|
7333
|
+
action: "response",
|
|
7334
|
+
content: chunk.text,
|
|
7335
|
+
finished: false,
|
|
7336
|
+
promptContext
|
|
7337
|
+
});
|
|
7338
|
+
}
|
|
7339
|
+
} else if (chunk.type === "reasoning-start") {
|
|
6839
7340
|
streamingMessageIds.add(responseMessageId);
|
|
6840
7341
|
await task.processResponseMessage({
|
|
6841
7342
|
id: responseMessageId,
|
|
6842
7343
|
action: "response",
|
|
6843
|
-
content:
|
|
7344
|
+
content: THINKING_RESPONSE_STAR_TAG,
|
|
6844
7345
|
finished: false,
|
|
6845
7346
|
promptContext
|
|
6846
7347
|
});
|
|
6847
|
-
hasReasoning =
|
|
6848
|
-
}
|
|
6849
|
-
} else if (chunk.type === "text-end") {
|
|
6850
|
-
responseMessageIndex++;
|
|
6851
|
-
} else if (chunk.type === "text-delta") {
|
|
6852
|
-
if (chunk.text.trim()) {
|
|
7348
|
+
hasReasoning = true;
|
|
7349
|
+
} else if (chunk.type === "reasoning-delta") {
|
|
6853
7350
|
streamingMessageIds.add(responseMessageId);
|
|
6854
7351
|
await task.processResponseMessage({
|
|
6855
7352
|
id: responseMessageId,
|
|
@@ -6858,51 +7355,53 @@ ${fileList}`
|
|
|
6858
7355
|
finished: false,
|
|
6859
7356
|
promptContext
|
|
6860
7357
|
});
|
|
7358
|
+
} else if (chunk.type === "tool-input-start") {
|
|
7359
|
+
task.addLogMessage("loading", "Preparing tool...", false, promptContext);
|
|
7360
|
+
streamingMessageIds.add(chunk.id);
|
|
7361
|
+
} else if (chunk.type === "tool-call") {
|
|
7362
|
+
task.addLogMessage("loading", "Executing tool...", false, promptContext);
|
|
7363
|
+
streamingMessageIds.add(chunk.toolCallId);
|
|
7364
|
+
} else if (chunk.type === "tool-result") {
|
|
7365
|
+
const [serverName, toolName] = extractServerNameToolName(chunk.toolName);
|
|
7366
|
+
const toolPromptContext = extractPromptContextFromToolResult(chunk.output) ?? promptContext;
|
|
7367
|
+
streamingMessageIds.add(chunk.toolCallId);
|
|
7368
|
+
task.addToolMessage(chunk.toolCallId, serverName, toolName, chunk.input, JSON.stringify(chunk.output), void 0, toolPromptContext);
|
|
7369
|
+
task.addLogMessage("loading", void 0, false, promptContext);
|
|
6861
7370
|
}
|
|
6862
|
-
}
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
} else if (
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
finished: false,
|
|
6879
|
-
promptContext
|
|
6880
|
-
});
|
|
6881
|
-
} else if (chunk.type === "tool-input-start") {
|
|
6882
|
-
task.addLogMessage("loading", "Preparing tool...", false, promptContext);
|
|
6883
|
-
streamingMessageIds.add(chunk.id);
|
|
6884
|
-
} else if (chunk.type === "tool-call") {
|
|
6885
|
-
task.addLogMessage("loading", "Executing tool...", false, promptContext);
|
|
6886
|
-
streamingMessageIds.add(chunk.toolCallId);
|
|
6887
|
-
} else if (chunk.type === "tool-result") {
|
|
6888
|
-
const [serverName, toolName] = extractServerNameToolName(chunk.toolName);
|
|
6889
|
-
const toolPromptContext = extractPromptContextFromToolResult(chunk.output) ?? promptContext;
|
|
6890
|
-
streamingMessageIds.add(chunk.toolCallId);
|
|
6891
|
-
task.addToolMessage(chunk.toolCallId, serverName, toolName, chunk.input, JSON.stringify(chunk.output), void 0, toolPromptContext);
|
|
6892
|
-
task.addLogMessage("loading", void 0, false, promptContext);
|
|
7371
|
+
}
|
|
7372
|
+
} catch (streamError) {
|
|
7373
|
+
if (effectiveAbortSignal?.aborted) {
|
|
7374
|
+
throw streamError;
|
|
7375
|
+
}
|
|
7376
|
+
if (isNetworkError(streamError) && retryCount < MAX_RETRIES) {
|
|
7377
|
+
logger.warn(`Network error during streaming, retrying (${retryCount + 1}/${MAX_RETRIES})...`, { error: streamError });
|
|
7378
|
+
iterationError = streamError;
|
|
7379
|
+
this.removeUnfinishedStreamingMessages(task, streamingMessageIds);
|
|
7380
|
+
task.addLogMessage("loading", "Network error occured. Retrying...");
|
|
7381
|
+
} else if (isNetworkError(streamError)) {
|
|
7382
|
+
const error = streamError;
|
|
7383
|
+
const underlyingMessage = error.cause instanceof Error ? error.cause.message : error.message;
|
|
7384
|
+
throw new Error(`Network error after ${MAX_RETRIES} retries (check your connection): ${underlyingMessage}`, { cause: streamError });
|
|
7385
|
+
} else {
|
|
7386
|
+
throw streamError;
|
|
6893
7387
|
}
|
|
6894
7388
|
}
|
|
6895
7389
|
}
|
|
6896
7390
|
if (iterationError) {
|
|
6897
|
-
logger.error("Error during
|
|
6898
|
-
if (iterationError
|
|
7391
|
+
logger.error("Error during iteration:", iterationError);
|
|
7392
|
+
if (isNetworkError(iterationError) && retryCount < MAX_RETRIES) {
|
|
7393
|
+
logger.info(`Network error, retrying (${retryCount + 1}/${MAX_RETRIES})...`);
|
|
7394
|
+
this.removeUnfinishedStreamingMessages(task, streamingMessageIds);
|
|
7395
|
+
retryCount++;
|
|
7396
|
+
continue;
|
|
7397
|
+
} else if (iterationError instanceof ai.APICallError && iterationError.isRetryable && this.modelManager.isRetryable(resolvedProvider, modelName, iterationError)) {
|
|
6899
7398
|
continue;
|
|
6900
7399
|
} else {
|
|
6901
7400
|
break;
|
|
6902
7401
|
}
|
|
6903
7402
|
}
|
|
6904
|
-
const newMessages = this.filterResultMessages(
|
|
6905
|
-
messages.push(...
|
|
7403
|
+
const newMessages = this.filterResultMessages(currentStepMessages);
|
|
7404
|
+
messages.push(...currentStepMessages);
|
|
6906
7405
|
resultMessages.push(...newMessages);
|
|
6907
7406
|
if (includeInContext) {
|
|
6908
7407
|
try {
|
|
@@ -6928,7 +7427,7 @@ ${fileList}`
|
|
|
6928
7427
|
retryCount++;
|
|
6929
7428
|
continue;
|
|
6930
7429
|
}
|
|
6931
|
-
const lastMessage =
|
|
7430
|
+
const lastMessage = currentStepMessages[currentStepMessages.length - 1];
|
|
6932
7431
|
if (finishReason === "stop" && lastMessage?.role === "tool") {
|
|
6933
7432
|
logger.debug('Finish reason is "stop" but last message is a tool call. Retrying...');
|
|
6934
7433
|
retryCount++;
|
|
@@ -6990,6 +7489,9 @@ ${fileList}`
|
|
|
6990
7489
|
} else {
|
|
6991
7490
|
task.addLogMessage("error", `${error instanceof Error ? error.message : String(error)}`, false, promptContext);
|
|
6992
7491
|
}
|
|
7492
|
+
await task.updateTask({
|
|
7493
|
+
state: DefaultTaskState.Interrupted
|
|
7494
|
+
});
|
|
6993
7495
|
} finally {
|
|
6994
7496
|
if (controllerId) {
|
|
6995
7497
|
this.abortControllers.delete(controllerId);
|
|
@@ -7286,11 +7788,9 @@ ${fileList}`
|
|
|
7286
7788
|
optimizedMessages.unshift({ role: "system", content: systemPrompt });
|
|
7287
7789
|
const chatMessages = optimizedMessages.map((msg) => ({
|
|
7288
7790
|
role: msg.role === "tool" ? "user" : msg.role,
|
|
7289
|
-
|
|
7290
|
-
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
7291
|
-
// Handle potential non-string content if necessary
|
|
7791
|
+
content: msg.content
|
|
7292
7792
|
}));
|
|
7293
|
-
return
|
|
7793
|
+
return estimateMessageTokens(chatMessages);
|
|
7294
7794
|
} catch (error) {
|
|
7295
7795
|
logger.error(`Error counting tokens: ${error}`);
|
|
7296
7796
|
return 0;
|
|
@@ -7384,32 +7884,80 @@ ${fileList}`
|
|
|
7384
7884
|
...message,
|
|
7385
7885
|
id: currentResponseId,
|
|
7386
7886
|
usageReport,
|
|
7387
|
-
promptContext
|
|
7887
|
+
promptContext,
|
|
7888
|
+
timestamp: Date.now()
|
|
7388
7889
|
});
|
|
7389
7890
|
} else if (message.role === "tool") {
|
|
7390
7891
|
messages.push({
|
|
7391
7892
|
...message,
|
|
7392
7893
|
// @ts-expect-error the id is there
|
|
7393
7894
|
id: message.id || uuid.v4(),
|
|
7394
|
-
promptContext
|
|
7895
|
+
promptContext,
|
|
7896
|
+
timestamp: Date.now()
|
|
7395
7897
|
});
|
|
7396
7898
|
}
|
|
7397
7899
|
});
|
|
7398
7900
|
return messages;
|
|
7399
7901
|
}
|
|
7400
7902
|
async compactMessagesIfNeeded(task, profile, provider, model, userRequestMessage, contextMessages, contextFiles, messages, resultMessages, promptContext, abortSignal) {
|
|
7401
|
-
const
|
|
7402
|
-
const
|
|
7403
|
-
|
|
7404
|
-
|
|
7405
|
-
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7903
|
+
const taskSettings = this.store.getSettings().taskSettings;
|
|
7904
|
+
const thresholdConfig = {
|
|
7905
|
+
percentage: profile.autoCompactThresholdPercentage ?? taskSettings.contextCompactingThreshold.percentage,
|
|
7906
|
+
tokens: profile.autoCompactThresholdTokens ?? taskSettings.contextCompactingThreshold.tokens
|
|
7907
|
+
};
|
|
7908
|
+
const taskTokensOverride = task.task.contextCompactingThresholdTokens;
|
|
7909
|
+
let contextCompactionType = profile.autoCompactionType ?? taskSettings.contextCompactionType ?? ContextCompactionType.Compact;
|
|
7910
|
+
if (profile.isSubagent && contextCompactionType === ContextCompactionType.Handoff) {
|
|
7911
|
+
contextCompactionType = ContextCompactionType.Compact;
|
|
7912
|
+
}
|
|
7913
|
+
logger.debug("Compaction threshold", {
|
|
7914
|
+
thresholdConfig,
|
|
7915
|
+
profile: {
|
|
7916
|
+
autoCompactThresholdPercentage: profile.autoCompactThresholdPercentage,
|
|
7917
|
+
autoCompactThresholdTokens: profile.autoCompactThresholdTokens
|
|
7918
|
+
},
|
|
7919
|
+
taskSettings: {
|
|
7920
|
+
contextCompactingThreshold: taskSettings.contextCompactingThreshold
|
|
7921
|
+
},
|
|
7922
|
+
taskTokensOverride,
|
|
7923
|
+
contextCompactionType
|
|
7924
|
+
});
|
|
7925
|
+
const lastAssistantMessage = [...resultMessages].reverse().find((m) => m.role === "assistant");
|
|
7926
|
+
const usageReport = lastAssistantMessage?.usageReport;
|
|
7927
|
+
const maxTokens = this.modelManager.getModelSettings(provider.id, model)?.maxInputTokens;
|
|
7928
|
+
if (!usageReport || !maxTokens) {
|
|
7929
|
+
logger.debug("No usageReport or maxTokens", {
|
|
7930
|
+
usageReport,
|
|
7931
|
+
maxTokens
|
|
7932
|
+
});
|
|
7933
|
+
return true;
|
|
7934
|
+
}
|
|
7935
|
+
const totalTokens = usageReport.sentTokens + usageReport.receivedTokens + (usageReport.cacheReadTokens ?? 0);
|
|
7936
|
+
let effectiveThreshold;
|
|
7937
|
+
let thresholdDescription;
|
|
7938
|
+
if (taskTokensOverride !== void 0) {
|
|
7939
|
+
if (taskTokensOverride === 0) {
|
|
7940
|
+
return true;
|
|
7941
|
+
}
|
|
7942
|
+
effectiveThreshold = taskTokensOverride;
|
|
7943
|
+
thresholdDescription = `task override: ${taskTokensOverride}`;
|
|
7944
|
+
} else {
|
|
7945
|
+
if (thresholdConfig.percentage === 0) {
|
|
7946
|
+
return true;
|
|
7947
|
+
}
|
|
7948
|
+
const percentageThreshold = maxTokens * thresholdConfig.percentage / 100;
|
|
7949
|
+
const tokenThreshold = thresholdConfig.tokens;
|
|
7950
|
+
effectiveThreshold = Math.min(percentageThreshold, tokenThreshold);
|
|
7951
|
+
thresholdDescription = `percentage: ${percentageThreshold}, tokens: ${tokenThreshold}`;
|
|
7952
|
+
}
|
|
7953
|
+
logger.debug("Checking total tokens vs effective threshold", {
|
|
7954
|
+
totalTokens,
|
|
7955
|
+
effectiveThreshold,
|
|
7956
|
+
thresholdDescription
|
|
7957
|
+
});
|
|
7958
|
+
if (totalTokens > effectiveThreshold) {
|
|
7411
7959
|
logger.info(
|
|
7412
|
-
`Token usage ${totalTokens} exceeds threshold of ${
|
|
7960
|
+
`Token usage ${totalTokens} exceeds effective threshold of ${effectiveThreshold} (${thresholdDescription}). Compacting conversation with type: ${contextCompactionType}.`
|
|
7413
7961
|
);
|
|
7414
7962
|
if (contextCompactionType === ContextCompactionType.Compact) {
|
|
7415
7963
|
await task.compactConversation(
|
|
@@ -7425,15 +7973,24 @@ ${fileList}`
|
|
|
7425
7973
|
messages.length = 0;
|
|
7426
7974
|
resultMessages.length = 0;
|
|
7427
7975
|
messages.push(...await this.prepareMessages(task, profile, await task.getContextMessages(), contextFiles));
|
|
7976
|
+
const continuationText = `Based on your compacted summary of our previous conversation, please continue our work with my request:
|
|
7977
|
+
|
|
7978
|
+
${extractTextContent(userRequestMessage.content)}`;
|
|
7979
|
+
const originalImageParts = Array.isArray(userRequestMessage.content) ? userRequestMessage.content.filter((part) => part.type === "image") : [];
|
|
7428
7980
|
resultMessages.push({
|
|
7429
7981
|
id: uuid.v4(),
|
|
7430
7982
|
role: "user",
|
|
7431
|
-
content:
|
|
7432
|
-
|
|
7433
|
-
${userRequestMessage.content}`,
|
|
7983
|
+
content: originalImageParts.length > 0 ? [{ type: "text", text: continuationText }, ...originalImageParts] : continuationText,
|
|
7434
7984
|
promptContext
|
|
7435
7985
|
});
|
|
7436
7986
|
messages.push(...resultMessages);
|
|
7987
|
+
} else if (contextCompactionType === ContextCompactionType.Smart) {
|
|
7988
|
+
const compactedMessages = await task.smartCompactConversation([...contextMessages, ...resultMessages], "Previous conversation has been compacted.");
|
|
7989
|
+
contextMessages.length = 0;
|
|
7990
|
+
messages.length = 0;
|
|
7991
|
+
resultMessages.length = 0;
|
|
7992
|
+
contextMessages.push(...compactedMessages);
|
|
7993
|
+
messages.push(...await this.prepareMessages(task, profile, compactedMessages, contextFiles));
|
|
7437
7994
|
} else if (contextCompactionType === ContextCompactionType.Handoff) {
|
|
7438
7995
|
await task.handoffConversation(
|
|
7439
7996
|
"agent",
|
|
@@ -7449,6 +8006,52 @@ ${userRequestMessage.content}`,
|
|
|
7449
8006
|
return true;
|
|
7450
8007
|
}
|
|
7451
8008
|
}
|
|
8009
|
+
let isDockerCache;
|
|
8010
|
+
const isDocker = () => {
|
|
8011
|
+
if (isDockerCache !== void 0) {
|
|
8012
|
+
return isDockerCache;
|
|
8013
|
+
}
|
|
8014
|
+
try {
|
|
8015
|
+
isDockerCache = fs__namespace.existsSync("/.dockerenv") || fs__namespace.existsSync("/run/.containerenv");
|
|
8016
|
+
} catch {
|
|
8017
|
+
isDockerCache = false;
|
|
8018
|
+
}
|
|
8019
|
+
return isDockerCache;
|
|
8020
|
+
};
|
|
8021
|
+
const isNetworkDrive = (drivePath) => {
|
|
8022
|
+
try {
|
|
8023
|
+
const driveLetter = drivePath.charAt(0).toUpperCase();
|
|
8024
|
+
const output = child_process.execSync(`net use ${driveLetter}:`, {
|
|
8025
|
+
encoding: "utf8",
|
|
8026
|
+
timeout: 5e3,
|
|
8027
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8028
|
+
});
|
|
8029
|
+
return output.includes("\\\\") || output.includes("//");
|
|
8030
|
+
} catch {
|
|
8031
|
+
return false;
|
|
8032
|
+
}
|
|
8033
|
+
};
|
|
8034
|
+
const shouldUsePolling = (watchPath, mode = FileWatchMode.Auto) => {
|
|
8035
|
+
if (mode === FileWatchMode.Polling) {
|
|
8036
|
+
return true;
|
|
8037
|
+
}
|
|
8038
|
+
if (mode === FileWatchMode.Native) {
|
|
8039
|
+
return false;
|
|
8040
|
+
}
|
|
8041
|
+
if (isDocker()) {
|
|
8042
|
+
logger.debug(`[FileWatch] Docker environment detected, using polling for ${watchPath}`);
|
|
8043
|
+
return true;
|
|
8044
|
+
}
|
|
8045
|
+
if (process.platform === "win32") {
|
|
8046
|
+
const isUncPath = watchPath.startsWith("\\\\") || watchPath.startsWith("//");
|
|
8047
|
+
const isNetworkLetterDrive = /^[A-Za-z]:/.test(watchPath) && isNetworkDrive(watchPath);
|
|
8048
|
+
if (isUncPath || isNetworkLetterDrive) {
|
|
8049
|
+
logger.debug(`[FileWatch] Network path detected (${watchPath}), using polling`);
|
|
8050
|
+
return true;
|
|
8051
|
+
}
|
|
8052
|
+
}
|
|
8053
|
+
return false;
|
|
8054
|
+
};
|
|
7452
8055
|
const getGlobalAgentsDir = () => path__namespace.join(os.homedir(), AIDER_DESK_AGENTS_DIR);
|
|
7453
8056
|
const getProjectAgentsDir = (projectDir) => path__namespace.join(projectDir, AIDER_DESK_AGENTS_DIR);
|
|
7454
8057
|
const getAgentsDirForProfile = (profile) => profile.projectDir ? getProjectAgentsDir(profile.projectDir) : getGlobalAgentsDir();
|
|
@@ -7481,9 +8084,10 @@ const getAllRuleFilesForProfile = async (profile, dirName) => {
|
|
|
7481
8084
|
return ruleFiles;
|
|
7482
8085
|
};
|
|
7483
8086
|
class AgentProfileManager {
|
|
7484
|
-
constructor(eventManager, extensionManager) {
|
|
8087
|
+
constructor(eventManager, extensionManager, store) {
|
|
7485
8088
|
this.eventManager = eventManager;
|
|
7486
8089
|
this.extensionManager = extensionManager;
|
|
8090
|
+
this.store = store;
|
|
7487
8091
|
this.extensionsChangeListener = () => {
|
|
7488
8092
|
this.sendAgentProfilesUpdated();
|
|
7489
8093
|
};
|
|
@@ -7598,12 +8202,12 @@ class AgentProfileManager {
|
|
|
7598
8202
|
}
|
|
7599
8203
|
const watcher = chokidar.watch(agentsDir, {
|
|
7600
8204
|
persistent: true,
|
|
7601
|
-
usePolling:
|
|
8205
|
+
usePolling: shouldUsePolling(agentsDir, this.store.getSettings().fileWatchMode),
|
|
7602
8206
|
ignoreInitial: true
|
|
7603
8207
|
});
|
|
7604
8208
|
const rulesWatcher = chokidar.watch(path__namespace.join(agentsDir, "*", AIDER_DESK_RULES_DIR), {
|
|
7605
8209
|
persistent: true,
|
|
7606
|
-
usePolling:
|
|
8210
|
+
usePolling: shouldUsePolling(agentsDir, this.store.getSettings().fileWatchMode),
|
|
7607
8211
|
ignoreInitial: true
|
|
7608
8212
|
});
|
|
7609
8213
|
const reloadFunction = () => this.debounceReloadProfiles(agentsDir);
|
|
@@ -7874,6 +8478,37 @@ class AgentProfileManager {
|
|
|
7874
8478
|
}
|
|
7875
8479
|
return this.profiles.get(profileId)?.agentProfile;
|
|
7876
8480
|
}
|
|
8481
|
+
resolveAgentProfile(id) {
|
|
8482
|
+
const lowerId = id.toLowerCase();
|
|
8483
|
+
const directMatch = this.getProfile(id);
|
|
8484
|
+
if (directMatch) {
|
|
8485
|
+
return directMatch;
|
|
8486
|
+
}
|
|
8487
|
+
const allProfiles = this.getAllProfiles();
|
|
8488
|
+
for (const profile of allProfiles) {
|
|
8489
|
+
if (profile.name.toLowerCase() === lowerId) {
|
|
8490
|
+
return profile;
|
|
8491
|
+
}
|
|
8492
|
+
}
|
|
8493
|
+
for (const context of this.profiles.values()) {
|
|
8494
|
+
if (context.dirName.toLowerCase() === lowerId) {
|
|
8495
|
+
return context.agentProfile;
|
|
8496
|
+
}
|
|
8497
|
+
}
|
|
8498
|
+
const slugifiedId = deriveDirName(id, /* @__PURE__ */ new Set()).toLowerCase();
|
|
8499
|
+
for (const context of this.profiles.values()) {
|
|
8500
|
+
if (context.dirName.toLowerCase() === slugifiedId) {
|
|
8501
|
+
return context.agentProfile;
|
|
8502
|
+
}
|
|
8503
|
+
}
|
|
8504
|
+
for (const profile of allProfiles) {
|
|
8505
|
+
const derivedName = deriveDirName(profile.name, /* @__PURE__ */ new Set()).toLowerCase();
|
|
8506
|
+
if (derivedName === slugifiedId) {
|
|
8507
|
+
return profile;
|
|
8508
|
+
}
|
|
8509
|
+
}
|
|
8510
|
+
return null;
|
|
8511
|
+
}
|
|
7877
8512
|
getOrderedProfiles(profileContexts) {
|
|
7878
8513
|
return profileContexts.sort((a, b) => {
|
|
7879
8514
|
const aIsProject = !!a.agentProfile.projectDir;
|
|
@@ -7970,6 +8605,22 @@ class AgentProfileManager {
|
|
|
7970
8605
|
this.directoryWatchers.clear();
|
|
7971
8606
|
this.profiles.clear();
|
|
7972
8607
|
}
|
|
8608
|
+
async settingsChanged(oldSettings, newSettings) {
|
|
8609
|
+
if (oldSettings.fileWatchMode === newSettings.fileWatchMode) {
|
|
8610
|
+
return;
|
|
8611
|
+
}
|
|
8612
|
+
const watchedDirs = Array.from(this.directoryWatchers.keys());
|
|
8613
|
+
for (const watcher of this.directoryWatchers.values()) {
|
|
8614
|
+
await watcher.close();
|
|
8615
|
+
}
|
|
8616
|
+
this.directoryWatchers.clear();
|
|
8617
|
+
for (const dir of watchedDirs) {
|
|
8618
|
+
if (dir.endsWith("-rules")) {
|
|
8619
|
+
continue;
|
|
8620
|
+
}
|
|
8621
|
+
await this.setupWatcherForDirectory(dir);
|
|
8622
|
+
}
|
|
8623
|
+
}
|
|
7973
8624
|
getDefaultAgentProfileId() {
|
|
7974
8625
|
for (const defaultProfile of DEFAULT_AGENT_PROFILES) {
|
|
7975
8626
|
if (this.profiles.has(defaultProfile.id)) {
|
|
@@ -8213,7 +8864,8 @@ const RunPromptSchema = zod.z.object({
|
|
|
8213
8864
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
8214
8865
|
taskId: zod.z.string().min(1, "Task ID is required"),
|
|
8215
8866
|
prompt: zod.z.string().min(1, "Prompt is required"),
|
|
8216
|
-
mode: zod.z.string().optional()
|
|
8867
|
+
mode: zod.z.string().optional(),
|
|
8868
|
+
images: zod.z.array(zod.z.string()).optional()
|
|
8217
8869
|
});
|
|
8218
8870
|
const SavePromptSchema = zod.z.object({
|
|
8219
8871
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
@@ -8233,8 +8885,8 @@ class PromptApi extends BaseApi {
|
|
|
8233
8885
|
if (!parsed) {
|
|
8234
8886
|
return;
|
|
8235
8887
|
}
|
|
8236
|
-
const { projectDir, taskId, prompt, mode } = parsed;
|
|
8237
|
-
const responses = await this.eventsHandler.runPrompt(projectDir, taskId, prompt, mode);
|
|
8888
|
+
const { projectDir, taskId, prompt, mode, images } = parsed;
|
|
8889
|
+
const responses = await this.eventsHandler.runPrompt(projectDir, taskId, prompt, mode, images);
|
|
8238
8890
|
res.status(200).json(responses);
|
|
8239
8891
|
})
|
|
8240
8892
|
);
|
|
@@ -8422,6 +9074,10 @@ const ClearContextSchema = zod.z.object({
|
|
|
8422
9074
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
8423
9075
|
taskId: zod.z.string().min(1, "Task id is required")
|
|
8424
9076
|
});
|
|
9077
|
+
const UndoContextChangeSchema = zod.z.object({
|
|
9078
|
+
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
9079
|
+
taskId: zod.z.string().min(1, "Task id is required")
|
|
9080
|
+
});
|
|
8425
9081
|
const AnswerQuestionSchema = zod.z.object({
|
|
8426
9082
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
8427
9083
|
taskId: zod.z.string().min(1, "Task id is required"),
|
|
@@ -8500,7 +9156,8 @@ const RedoUserPromptSchema = zod.z.object({
|
|
|
8500
9156
|
taskId: zod.z.string().min(1, "Task id is required"),
|
|
8501
9157
|
messageId: zod.z.string().min(1, "Message id is required"),
|
|
8502
9158
|
mode: zod.z.string().min(1, "Mode is required"),
|
|
8503
|
-
updatedPrompt: zod.z.string().optional()
|
|
9159
|
+
updatedPrompt: zod.z.string().optional(),
|
|
9160
|
+
updatedImages: zod.z.array(zod.z.string()).optional()
|
|
8504
9161
|
});
|
|
8505
9162
|
const ResumeTaskSchema = zod.z.object({
|
|
8506
9163
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
@@ -8546,7 +9203,8 @@ const InitProjectRulesFileSchema = zod.z.object({
|
|
|
8546
9203
|
const CreateNewTaskSchema = zod.z.object({
|
|
8547
9204
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
8548
9205
|
parentId: zod.z.string().nullable().optional(),
|
|
8549
|
-
name: zod.z.string().optional()
|
|
9206
|
+
name: zod.z.string().optional(),
|
|
9207
|
+
activate: zod.z.boolean().optional()
|
|
8550
9208
|
});
|
|
8551
9209
|
const UpdateTaskSchema = zod.z.object({
|
|
8552
9210
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
@@ -8607,6 +9265,10 @@ const HandoffConversationSchema = zod.z.object({
|
|
|
8607
9265
|
taskId: zod.z.string().min(1, "Task id is required"),
|
|
8608
9266
|
focus: zod.z.string().optional()
|
|
8609
9267
|
});
|
|
9268
|
+
const SmartCompactConversationSchema = zod.z.object({
|
|
9269
|
+
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
9270
|
+
taskId: zod.z.string().min(1, "Task id is required")
|
|
9271
|
+
});
|
|
8610
9272
|
const ChangeRequestItemSchema = zod.z.object({
|
|
8611
9273
|
filename: zod.z.string().min(1, "Filename is required"),
|
|
8612
9274
|
lineNumber: zod.z.number().int().min(1, "Line number is required"),
|
|
@@ -8727,8 +9389,8 @@ class ProjectApi extends BaseApi {
|
|
|
8727
9389
|
if (!parsed) {
|
|
8728
9390
|
return;
|
|
8729
9391
|
}
|
|
8730
|
-
const { projectDir, taskId, messageId, mode, updatedPrompt } = parsed;
|
|
8731
|
-
await this.eventsHandler.redoUserPrompt(projectDir, taskId, messageId, mode, updatedPrompt);
|
|
9392
|
+
const { projectDir, taskId, messageId, mode, updatedPrompt, updatedImages } = parsed;
|
|
9393
|
+
await this.eventsHandler.redoUserPrompt(projectDir, taskId, messageId, mode, updatedPrompt, updatedImages);
|
|
8732
9394
|
res.status(200).json({ message: "Redo user prompt initiated" });
|
|
8733
9395
|
})
|
|
8734
9396
|
);
|
|
@@ -8840,8 +9502,8 @@ class ProjectApi extends BaseApi {
|
|
|
8840
9502
|
if (!parsed) {
|
|
8841
9503
|
return;
|
|
8842
9504
|
}
|
|
8843
|
-
const { projectDir, parentId, name } = parsed;
|
|
8844
|
-
const params = { parentId, name };
|
|
9505
|
+
const { projectDir, parentId, name, activate } = parsed;
|
|
9506
|
+
const params = { parentId, name, activate };
|
|
8845
9507
|
const task = await this.eventsHandler.createNewTask(projectDir, params);
|
|
8846
9508
|
res.status(200).json(task);
|
|
8847
9509
|
})
|
|
@@ -9013,6 +9675,30 @@ class ProjectApi extends BaseApi {
|
|
|
9013
9675
|
res.status(200).json({ message: "Conversation compacted" });
|
|
9014
9676
|
})
|
|
9015
9677
|
);
|
|
9678
|
+
router.post(
|
|
9679
|
+
"/project/smart-compact-conversation",
|
|
9680
|
+
this.handleRequest(async (req, res) => {
|
|
9681
|
+
const parsed = this.validateRequest(SmartCompactConversationSchema, req.body, res);
|
|
9682
|
+
if (!parsed) {
|
|
9683
|
+
return;
|
|
9684
|
+
}
|
|
9685
|
+
const { projectDir, taskId } = parsed;
|
|
9686
|
+
await this.eventsHandler.smartCompactConversation(projectDir, taskId);
|
|
9687
|
+
res.status(200).json({ message: "Conversation smart-compacted" });
|
|
9688
|
+
})
|
|
9689
|
+
);
|
|
9690
|
+
router.post(
|
|
9691
|
+
"/project/undo-context-change",
|
|
9692
|
+
this.handleRequest(async (req, res) => {
|
|
9693
|
+
const parsed = this.validateRequest(UndoContextChangeSchema, req.body, res);
|
|
9694
|
+
if (!parsed) {
|
|
9695
|
+
return;
|
|
9696
|
+
}
|
|
9697
|
+
const { projectDir, taskId } = parsed;
|
|
9698
|
+
const undone = await this.eventsHandler.undoContextChange(projectDir, taskId);
|
|
9699
|
+
res.status(200).json({ undone });
|
|
9700
|
+
})
|
|
9701
|
+
);
|
|
9016
9702
|
router.post(
|
|
9017
9703
|
"/project/handoff-conversation",
|
|
9018
9704
|
this.handleRequest(async (req, res) => {
|
|
@@ -10883,10 +11569,11 @@ class ShellCommandError extends Error {
|
|
|
10883
11569
|
}
|
|
10884
11570
|
const execAsync$1 = util.promisify(child_process.exec);
|
|
10885
11571
|
class CustomCommandManager {
|
|
10886
|
-
constructor(project, eventManager, extensionManager) {
|
|
11572
|
+
constructor(project, eventManager, extensionManager, store) {
|
|
10887
11573
|
this.project = project;
|
|
10888
11574
|
this.eventManager = eventManager;
|
|
10889
11575
|
this.extensionManager = extensionManager;
|
|
11576
|
+
this.store = store;
|
|
10890
11577
|
}
|
|
10891
11578
|
commands = /* @__PURE__ */ new Map();
|
|
10892
11579
|
watchers = [];
|
|
@@ -10926,7 +11613,7 @@ class CustomCommandManager {
|
|
|
10926
11613
|
}
|
|
10927
11614
|
const watcher = chokidar.watch(commandsDir, {
|
|
10928
11615
|
persistent: true,
|
|
10929
|
-
usePolling:
|
|
11616
|
+
usePolling: shouldUsePolling(commandsDir, this.store.getSettings().fileWatchMode),
|
|
10930
11617
|
ignoreInitial: true
|
|
10931
11618
|
});
|
|
10932
11619
|
watcher.on("add", async () => {
|
|
@@ -10984,14 +11671,16 @@ class CustomCommandManager {
|
|
|
10984
11671
|
const args = Array.isArray(parsed.arguments) ? parsed.arguments : [];
|
|
10985
11672
|
const template = parsed.__content?.trim() || "";
|
|
10986
11673
|
const includeContext = typeof parsed.includeContext === "boolean" ? parsed.includeContext : true;
|
|
10987
|
-
const
|
|
11674
|
+
const autonomyMode = typeof parsed.autonomyMode === "string" && ["manual", "guided", "autonomous"].includes(parsed.autonomyMode) ? parsed.autonomyMode : void 0;
|
|
11675
|
+
const skills = typeof parsed.skills === "string" && parsed.skills.trim() ? parsed.skills.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
10988
11676
|
commands.set(name, {
|
|
10989
11677
|
name,
|
|
10990
11678
|
description: parsed.description || "Not specified",
|
|
10991
11679
|
arguments: args,
|
|
10992
11680
|
template,
|
|
10993
11681
|
includeContext,
|
|
10994
|
-
|
|
11682
|
+
autonomyMode,
|
|
11683
|
+
skills
|
|
10995
11684
|
});
|
|
10996
11685
|
} catch (err) {
|
|
10997
11686
|
logger.error(`Failed to parse command file ${filePath}: ${err}`);
|
|
@@ -11123,6 +11812,14 @@ class CustomCommandManager {
|
|
|
11123
11812
|
this.watchers.forEach((watcher) => watcher.close());
|
|
11124
11813
|
this.watchers = [];
|
|
11125
11814
|
}
|
|
11815
|
+
async settingsChanged(oldSettings, newSettings) {
|
|
11816
|
+
if (oldSettings.fileWatchMode === newSettings.fileWatchMode) {
|
|
11817
|
+
return;
|
|
11818
|
+
}
|
|
11819
|
+
this.watchers.forEach((watcher) => watcher.close());
|
|
11820
|
+
this.watchers = [];
|
|
11821
|
+
await this.setupFileWatchers();
|
|
11822
|
+
}
|
|
11126
11823
|
}
|
|
11127
11824
|
const migrateContextMessage = (message) => {
|
|
11128
11825
|
if (Array.isArray(message.content)) {
|
|
@@ -11225,10 +11922,20 @@ class ContextManager {
|
|
|
11225
11922
|
}
|
|
11226
11923
|
messages;
|
|
11227
11924
|
files;
|
|
11925
|
+
undoSnapshot = null;
|
|
11228
11926
|
loadPromise = null;
|
|
11229
11927
|
loaded = false;
|
|
11230
11928
|
autosaveEnabled = false;
|
|
11231
11929
|
storagePath;
|
|
11930
|
+
hasUndoSnapshot() {
|
|
11931
|
+
return this.undoSnapshot !== null;
|
|
11932
|
+
}
|
|
11933
|
+
undoContextChange() {
|
|
11934
|
+
const snapshot = this.undoSnapshot;
|
|
11935
|
+
this.undoSnapshot = null;
|
|
11936
|
+
this.task.sendContextInfoUpdated();
|
|
11937
|
+
return snapshot;
|
|
11938
|
+
}
|
|
11232
11939
|
enableAutosave() {
|
|
11233
11940
|
logger.debug("Enabling autosave for task", { taskId: this.taskId });
|
|
11234
11941
|
this.autosaveEnabled = true;
|
|
@@ -11247,7 +11954,8 @@ class ContextManager {
|
|
|
11247
11954
|
id: uuid.v4(),
|
|
11248
11955
|
role: roleOrMessage,
|
|
11249
11956
|
content: content || "",
|
|
11250
|
-
usageReport
|
|
11957
|
+
usageReport,
|
|
11958
|
+
timestamp: Date.now()
|
|
11251
11959
|
};
|
|
11252
11960
|
} else {
|
|
11253
11961
|
message = roleOrMessage;
|
|
@@ -11260,6 +11968,10 @@ class ContextManager {
|
|
|
11260
11968
|
}
|
|
11261
11969
|
this.messages.push(message);
|
|
11262
11970
|
logger.debug(`Task ${this.taskId}: Added ${message.role} message. Total messages: ${this.messages.length}`);
|
|
11971
|
+
if (this.undoSnapshot !== null) {
|
|
11972
|
+
this.undoSnapshot = null;
|
|
11973
|
+
this.task.sendContextInfoUpdated();
|
|
11974
|
+
}
|
|
11263
11975
|
this.autosave();
|
|
11264
11976
|
}
|
|
11265
11977
|
async isFileIgnored(contextFile) {
|
|
@@ -11277,7 +11989,14 @@ class ContextManager {
|
|
|
11277
11989
|
});
|
|
11278
11990
|
return [];
|
|
11279
11991
|
}
|
|
11280
|
-
|
|
11992
|
+
const existingContextFile = this.findExistingContextFile(absolutePath);
|
|
11993
|
+
if (existingContextFile) {
|
|
11994
|
+
const readOnly = contextFile.readOnly ?? false;
|
|
11995
|
+
if (existingContextFile.readOnly !== readOnly) {
|
|
11996
|
+
existingContextFile.readOnly = readOnly;
|
|
11997
|
+
this.autosave();
|
|
11998
|
+
return [existingContextFile];
|
|
11999
|
+
}
|
|
11281
12000
|
return [];
|
|
11282
12001
|
}
|
|
11283
12002
|
const isDir = await isDirectory(absolutePath);
|
|
@@ -11336,11 +12055,11 @@ class ContextManager {
|
|
|
11336
12055
|
return this.task.resolveContextFilePath(relativePath);
|
|
11337
12056
|
}
|
|
11338
12057
|
/**
|
|
11339
|
-
*
|
|
12058
|
+
* Finds an already-added context file matching the given resolved absolute path, if any.
|
|
11340
12059
|
* Accounts for files that may have been resolved from either taskDir or projectDir.
|
|
11341
12060
|
*/
|
|
11342
|
-
|
|
11343
|
-
return this.files.
|
|
12061
|
+
findExistingContextFile(absolutePath) {
|
|
12062
|
+
return this.files.find((file) => {
|
|
11344
12063
|
const taskDirResolved = path.resolve(this.task.getTaskDir(), file.path);
|
|
11345
12064
|
const projectDirResolved = path.resolve(this.task.getProjectDir(), file.path);
|
|
11346
12065
|
return taskDirResolved === absolutePath || projectDirResolved === absolutePath;
|
|
@@ -11394,6 +12113,10 @@ class ContextManager {
|
|
|
11394
12113
|
messages: contextMessages.length,
|
|
11395
12114
|
save
|
|
11396
12115
|
});
|
|
12116
|
+
if (this.messages.length > 0 && this.messages !== contextMessages) {
|
|
12117
|
+
this.undoSnapshot = [...this.messages];
|
|
12118
|
+
this.task.sendContextInfoUpdated();
|
|
12119
|
+
}
|
|
11397
12120
|
this.messages = contextMessages;
|
|
11398
12121
|
if (save) {
|
|
11399
12122
|
this.autosave();
|
|
@@ -11423,8 +12146,12 @@ class ContextManager {
|
|
|
11423
12146
|
this.autosave();
|
|
11424
12147
|
}
|
|
11425
12148
|
}
|
|
11426
|
-
clearMessages(save = true) {
|
|
12149
|
+
clearMessages(save = true, createSnapshot = true) {
|
|
11427
12150
|
logger.debug("Clearing task messages", { taskId: this.taskId });
|
|
12151
|
+
if (createSnapshot && this.messages.length > 0) {
|
|
12152
|
+
this.undoSnapshot = [...this.messages];
|
|
12153
|
+
this.task.sendContextInfoUpdated();
|
|
12154
|
+
}
|
|
11428
12155
|
this.messages = [];
|
|
11429
12156
|
if (save) {
|
|
11430
12157
|
this.autosave();
|
|
@@ -11721,6 +12448,34 @@ ${JSON.stringify(part.output.value)}`
|
|
|
11721
12448
|
throw error;
|
|
11722
12449
|
}
|
|
11723
12450
|
}
|
|
12451
|
+
/**
|
|
12452
|
+
* Creates a backup of the current context.json file before a destructive operation
|
|
12453
|
+
* (e.g., smart compaction). Backups are named context.backup.001.json, context.backup.002.json, etc.
|
|
12454
|
+
* For debugging purposes only.
|
|
12455
|
+
*/
|
|
12456
|
+
async backupContext() {
|
|
12457
|
+
try {
|
|
12458
|
+
const dir = path.dirname(this.storagePath);
|
|
12459
|
+
const files = await fs$1.promises.readdir(dir);
|
|
12460
|
+
const backupPattern = /^context\.backup\.(\d+)\.json$/;
|
|
12461
|
+
let maxNumber = 0;
|
|
12462
|
+
for (const file of files) {
|
|
12463
|
+
const match = backupPattern.exec(file);
|
|
12464
|
+
if (match) {
|
|
12465
|
+
const num = parseInt(match[1], 10);
|
|
12466
|
+
if (num > maxNumber) {
|
|
12467
|
+
maxNumber = num;
|
|
12468
|
+
}
|
|
12469
|
+
}
|
|
12470
|
+
}
|
|
12471
|
+
const nextNumber = String(maxNumber + 1).padStart(3, "0");
|
|
12472
|
+
const backupPath = path.join(dir, `context.backup.${nextNumber}.json`);
|
|
12473
|
+
await fs$1.promises.copyFile(this.storagePath, backupPath);
|
|
12474
|
+
logger.debug(`Task context backed up to ${backupPath}`, { taskId: this.taskId });
|
|
12475
|
+
} catch (error) {
|
|
12476
|
+
logger.error("Failed to backup task context:", { error, taskId: this.taskId });
|
|
12477
|
+
}
|
|
12478
|
+
}
|
|
11724
12479
|
async cleanupContext() {
|
|
11725
12480
|
const existingFiles = [];
|
|
11726
12481
|
for (const file of this.files) {
|
|
@@ -11794,8 +12549,8 @@ ${JSON.stringify(part.output.value)}`
|
|
|
11794
12549
|
this.loaded = true;
|
|
11795
12550
|
await this.cleanupContext();
|
|
11796
12551
|
}
|
|
11797
|
-
async loadMessages(messages) {
|
|
11798
|
-
await this.task.clearContext(false, false);
|
|
12552
|
+
async loadMessages(messages, updateTaskState = true) {
|
|
12553
|
+
await this.task.clearContext(false, false, updateTaskState, false);
|
|
11799
12554
|
this.messages = messages;
|
|
11800
12555
|
const messagesData = this.getContextMessagesData(messages);
|
|
11801
12556
|
for (const messageData of messagesData) {
|
|
@@ -11866,7 +12621,8 @@ ${JSON.stringify(part.output.value)}`
|
|
|
11866
12621
|
commitMessage: response.commitMessage,
|
|
11867
12622
|
diff: response.diff,
|
|
11868
12623
|
usageReport: response.usageReport,
|
|
11869
|
-
promptContext: message.promptContext
|
|
12624
|
+
promptContext: message.promptContext,
|
|
12625
|
+
timestamp: message.timestamp
|
|
11870
12626
|
};
|
|
11871
12627
|
messagesData.push(responseCompletedData);
|
|
11872
12628
|
});
|
|
@@ -11905,7 +12661,8 @@ ${JSON.stringify(part.output.value)}`
|
|
|
11905
12661
|
baseDir: this.task.getProjectDir(),
|
|
11906
12662
|
taskId: this.taskId,
|
|
11907
12663
|
usageReport: subMessage.usageReport,
|
|
11908
|
-
promptContext: subMessage.promptContext
|
|
12664
|
+
promptContext: subMessage.promptContext,
|
|
12665
|
+
timestamp: subMessage.timestamp
|
|
11909
12666
|
};
|
|
11910
12667
|
messagesData.push(responseCompletedData);
|
|
11911
12668
|
}
|
|
@@ -11921,7 +12678,8 @@ ${JSON.stringify(part.output.value)}`
|
|
|
11921
12678
|
toolName: subToolName,
|
|
11922
12679
|
args: subPart.input,
|
|
11923
12680
|
usageReport: void 0,
|
|
11924
|
-
promptContext: subMessage.promptContext
|
|
12681
|
+
promptContext: subMessage.promptContext,
|
|
12682
|
+
timestamp: subMessage.timestamp
|
|
11925
12683
|
};
|
|
11926
12684
|
messagesData.push(toolData);
|
|
11927
12685
|
}
|
|
@@ -11935,7 +12693,8 @@ ${JSON.stringify(part.output.value)}`
|
|
|
11935
12693
|
baseDir: this.task.getProjectDir(),
|
|
11936
12694
|
taskId: this.taskId,
|
|
11937
12695
|
usageReport: subMessage.usageReport,
|
|
11938
|
-
promptContext: subMessage.promptContext
|
|
12696
|
+
promptContext: subMessage.promptContext,
|
|
12697
|
+
timestamp: subMessage.timestamp
|
|
11939
12698
|
};
|
|
11940
12699
|
messagesData.push(responseCompletedData);
|
|
11941
12700
|
}
|
|
@@ -11978,7 +12737,8 @@ ${JSON.stringify(part.output.value)}`
|
|
|
11978
12737
|
commitMessage: message.commitMessage,
|
|
11979
12738
|
diff: message.diff,
|
|
11980
12739
|
usageReport: message.usageReport,
|
|
11981
|
-
promptContext: message.promptContext
|
|
12740
|
+
promptContext: message.promptContext,
|
|
12741
|
+
timestamp: message.timestamp
|
|
11982
12742
|
};
|
|
11983
12743
|
messagesData.push(responseCompletedData);
|
|
11984
12744
|
reasoning = "";
|
|
@@ -12007,7 +12767,8 @@ ${JSON.stringify(part.output.value)}`
|
|
|
12007
12767
|
toolName,
|
|
12008
12768
|
args: toolCall.input,
|
|
12009
12769
|
usageReport: message.usageReport,
|
|
12010
|
-
promptContext: message.promptContext
|
|
12770
|
+
promptContext: message.promptContext,
|
|
12771
|
+
timestamp: message.timestamp
|
|
12011
12772
|
};
|
|
12012
12773
|
messagesData.push(toolData);
|
|
12013
12774
|
} else if (part.type === "tool-result") {
|
|
@@ -12041,19 +12802,27 @@ ${JSON.stringify(part.output.value)}`
|
|
|
12041
12802
|
taskId: this.taskId,
|
|
12042
12803
|
reflectedMessage: message.reflectedMessage,
|
|
12043
12804
|
usageReport: message.usageReport,
|
|
12044
|
-
promptContext: message.promptContext
|
|
12805
|
+
promptContext: message.promptContext,
|
|
12806
|
+
timestamp: message.timestamp
|
|
12045
12807
|
};
|
|
12046
12808
|
messagesData.push(responseCompletedData);
|
|
12047
12809
|
}
|
|
12048
12810
|
} else if (message.role === "user") {
|
|
12049
12811
|
const content = extractTextContent(message.content);
|
|
12812
|
+
const images = Array.isArray(message.content) ? message.content.filter((part) => part.type === "image").map((part) => {
|
|
12813
|
+
const data = part.image;
|
|
12814
|
+
const mediaType = part.mediaType || "image/png";
|
|
12815
|
+
return data.startsWith("data:") ? data : `data:${mediaType};base64,${data}`;
|
|
12816
|
+
}) : void 0;
|
|
12050
12817
|
const userMessageData = {
|
|
12051
12818
|
type: "user",
|
|
12052
12819
|
id: message.id || uuid.v4(),
|
|
12053
12820
|
baseDir: this.task.getProjectDir(),
|
|
12054
12821
|
taskId: this.taskId,
|
|
12055
12822
|
content,
|
|
12056
|
-
|
|
12823
|
+
images: images && images.length > 0 ? images : void 0,
|
|
12824
|
+
promptContext: message.promptContext,
|
|
12825
|
+
timestamp: message.timestamp
|
|
12057
12826
|
};
|
|
12058
12827
|
messagesData.push(userMessageData);
|
|
12059
12828
|
} else if (message.role === "tool" && Array.isArray(message.content)) {
|
|
@@ -12811,7 +13580,16 @@ class SkillManager {
|
|
|
12811
13580
|
loadSkillsFromDir(projectSkillsDir, "project"),
|
|
12812
13581
|
loadSkillsFromDir(AIDER_DESK_BUILTIN_SKILLS_DIR, "builtin")
|
|
12813
13582
|
]);
|
|
12814
|
-
|
|
13583
|
+
const allSkills = [...extensionSkills, ...projectSkills, ...globalSkills, ...builtinSkills];
|
|
13584
|
+
const seen = /* @__PURE__ */ new Set();
|
|
13585
|
+
const deduped = [];
|
|
13586
|
+
for (const skill of allSkills) {
|
|
13587
|
+
if (!seen.has(skill.name)) {
|
|
13588
|
+
seen.add(skill.name);
|
|
13589
|
+
deduped.push(skill);
|
|
13590
|
+
}
|
|
13591
|
+
}
|
|
13592
|
+
return deduped;
|
|
12815
13593
|
}
|
|
12816
13594
|
async getSkills(contextMessages) {
|
|
12817
13595
|
const skills = await this.loadAllSkills();
|
|
@@ -12960,7 +13738,13 @@ class WorktreeManager {
|
|
|
12960
13738
|
await execWithShellPath("git add -A", { cwd: projectPath });
|
|
12961
13739
|
} catch {
|
|
12962
13740
|
}
|
|
12963
|
-
|
|
13741
|
+
try {
|
|
13742
|
+
await execWithShellPath('git commit -m "Initial commit" --allow-empty', { cwd: projectPath });
|
|
13743
|
+
} catch {
|
|
13744
|
+
await execWithShellPath('git -c user.name="AiderDesk" -c user.email="aiderdesk@aiderdesk" commit -m "Initial commit" --allow-empty', {
|
|
13745
|
+
cwd: projectPath
|
|
13746
|
+
});
|
|
13747
|
+
}
|
|
12964
13748
|
}
|
|
12965
13749
|
let baseCommit;
|
|
12966
13750
|
let newBranchName;
|
|
@@ -14114,6 +14898,11 @@ Git output: ${err.stderr || err.stdout || err.message}`
|
|
|
14114
14898
|
logger.warn(`Failed to get files for commit ${commit.hash}:`, commitError);
|
|
14115
14899
|
}
|
|
14116
14900
|
}
|
|
14901
|
+
try {
|
|
14902
|
+
await execWithShellPath("git rev-parse HEAD", { cwd: worktreePath });
|
|
14903
|
+
} catch {
|
|
14904
|
+
return files;
|
|
14905
|
+
}
|
|
14117
14906
|
try {
|
|
14118
14907
|
const { stdout: uncommittedNumstat } = await execWithShellPath("git diff --numstat -z HEAD", {
|
|
14119
14908
|
cwd: worktreePath
|
|
@@ -14212,6 +15001,11 @@ Git output: ${err.stderr || err.stdout || err.message}`
|
|
|
14212
15001
|
* Non-worktree mode: simple uncommitted changes vs HEAD.
|
|
14213
15002
|
*/
|
|
14214
15003
|
async getNonWorktreeUpdatedFiles(worktreePath) {
|
|
15004
|
+
try {
|
|
15005
|
+
await execWithShellPath("git rev-parse HEAD", { cwd: worktreePath });
|
|
15006
|
+
} catch {
|
|
15007
|
+
return [];
|
|
15008
|
+
}
|
|
14215
15009
|
const { stdout } = await execWithShellPath("git diff --numstat -z HEAD", {
|
|
14216
15010
|
cwd: worktreePath
|
|
14217
15011
|
});
|
|
@@ -14606,11 +15400,668 @@ Git output: ${err.stderr || err.stdout || err.message}`
|
|
|
14606
15400
|
}
|
|
14607
15401
|
}
|
|
14608
15402
|
}
|
|
14609
|
-
async close(projectDir) {
|
|
14610
|
-
logger.info("Closing worktree manager");
|
|
14611
|
-
await this.pruneDeleted(projectDir);
|
|
15403
|
+
async close(projectDir) {
|
|
15404
|
+
logger.info("Closing worktree manager");
|
|
15405
|
+
await this.pruneDeleted(projectDir);
|
|
15406
|
+
}
|
|
15407
|
+
}
|
|
15408
|
+
const extractFilePath = (args) => {
|
|
15409
|
+
if (typeof args === "object" && args !== null && "filePath" in args) {
|
|
15410
|
+
return args.filePath;
|
|
15411
|
+
}
|
|
15412
|
+
return void 0;
|
|
15413
|
+
};
|
|
15414
|
+
const getToolOutputText = (output) => {
|
|
15415
|
+
if (!output) {
|
|
15416
|
+
return "";
|
|
15417
|
+
}
|
|
15418
|
+
if (output.type === "text") {
|
|
15419
|
+
return output.value;
|
|
15420
|
+
}
|
|
15421
|
+
if (output.type === "error-text") {
|
|
15422
|
+
return output.value;
|
|
15423
|
+
}
|
|
15424
|
+
return JSON.stringify(output.value);
|
|
15425
|
+
};
|
|
15426
|
+
const isErrorResult = (output) => {
|
|
15427
|
+
const text = getToolOutputText(output).toLowerCase();
|
|
15428
|
+
return text.includes("denied by user") || text.startsWith("error:") || text.includes("no files found") || text.includes("no matches found") || text.includes("operation was cancelled") || text.includes("warning:") || text.includes("already updated - no changes were needed") || text.includes("failed to");
|
|
15429
|
+
};
|
|
15430
|
+
const isNoOpResult = (output) => {
|
|
15431
|
+
const text = getToolOutputText(output);
|
|
15432
|
+
return text.includes("Already updated - no changes were needed");
|
|
15433
|
+
};
|
|
15434
|
+
const isPowerTool = (serverName) => {
|
|
15435
|
+
return serverName === POWER_TOOL_GROUP_NAME;
|
|
15436
|
+
};
|
|
15437
|
+
const getToolInfoFromToolMessage = (message) => {
|
|
15438
|
+
return message.content.filter((part) => part.type === "tool-result").map((part) => {
|
|
15439
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15440
|
+
return {
|
|
15441
|
+
messageIndex: -1,
|
|
15442
|
+
toolCallId: part.toolCallId,
|
|
15443
|
+
toolName,
|
|
15444
|
+
serverName,
|
|
15445
|
+
input: void 0,
|
|
15446
|
+
output: part.output
|
|
15447
|
+
};
|
|
15448
|
+
});
|
|
15449
|
+
};
|
|
15450
|
+
const findAssistantToolCall = (messages, toolCallId) => {
|
|
15451
|
+
for (let i = 0; i < messages.length; i++) {
|
|
15452
|
+
const msg = messages[i];
|
|
15453
|
+
if (msg.role !== "assistant") {
|
|
15454
|
+
continue;
|
|
15455
|
+
}
|
|
15456
|
+
if (!Array.isArray(msg.content)) {
|
|
15457
|
+
continue;
|
|
15458
|
+
}
|
|
15459
|
+
for (let j = 0; j < msg.content.length; j++) {
|
|
15460
|
+
const part = msg.content[j];
|
|
15461
|
+
if (part.type === "tool-call" && part.toolCallId === toolCallId) {
|
|
15462
|
+
const [, toolName] = extractServerNameToolName(part.toolName);
|
|
15463
|
+
return {
|
|
15464
|
+
messageId: msg.id,
|
|
15465
|
+
messageIndex: i,
|
|
15466
|
+
partIndex: j,
|
|
15467
|
+
toolCallId: part.toolCallId,
|
|
15468
|
+
toolName,
|
|
15469
|
+
input: part.input
|
|
15470
|
+
};
|
|
15471
|
+
}
|
|
15472
|
+
}
|
|
15473
|
+
}
|
|
15474
|
+
return void 0;
|
|
15475
|
+
};
|
|
15476
|
+
const cloneMessages = (messages) => {
|
|
15477
|
+
return messages.map((msg) => {
|
|
15478
|
+
if (msg.role === "tool") {
|
|
15479
|
+
return {
|
|
15480
|
+
...msg,
|
|
15481
|
+
content: msg.content.map((part) => ({ ...part }))
|
|
15482
|
+
};
|
|
15483
|
+
}
|
|
15484
|
+
return {
|
|
15485
|
+
...msg,
|
|
15486
|
+
content: Array.isArray(msg.content) ? msg.content.map((part) => ({ ...part })) : msg.content
|
|
15487
|
+
};
|
|
15488
|
+
});
|
|
15489
|
+
};
|
|
15490
|
+
const removeToolCallFromAssistant = (messages, toolCallId) => {
|
|
15491
|
+
const callInfo = findAssistantToolCall(messages, toolCallId);
|
|
15492
|
+
if (!callInfo) {
|
|
15493
|
+
return;
|
|
15494
|
+
}
|
|
15495
|
+
const assistantMsg = messages[callInfo.messageIndex];
|
|
15496
|
+
if (!Array.isArray(assistantMsg.content)) {
|
|
15497
|
+
return;
|
|
15498
|
+
}
|
|
15499
|
+
assistantMsg.content.splice(callInfo.partIndex, 1);
|
|
15500
|
+
const hasToolCalls = assistantMsg.content.some((p) => p.type === "tool-call");
|
|
15501
|
+
if (!hasToolCalls) {
|
|
15502
|
+
messages.splice(callInfo.messageIndex, 1);
|
|
15503
|
+
}
|
|
15504
|
+
};
|
|
15505
|
+
const removeToolResult = (messages, toolCallId) => {
|
|
15506
|
+
const toolMsgIdx = messages.findIndex((m) => m.role === "tool" && m.content.some((p) => p.type === "tool-result" && p.toolCallId === toolCallId));
|
|
15507
|
+
if (toolMsgIdx === -1) {
|
|
15508
|
+
return;
|
|
15509
|
+
}
|
|
15510
|
+
const toolMsg = messages[toolMsgIdx];
|
|
15511
|
+
const partIdx = toolMsg.content.findIndex((p) => p.type === "tool-result" && p.toolCallId === toolCallId);
|
|
15512
|
+
if (partIdx === -1) {
|
|
15513
|
+
return;
|
|
15514
|
+
}
|
|
15515
|
+
toolMsg.content.splice(partIdx, 1);
|
|
15516
|
+
if (toolMsg.content.length === 0) {
|
|
15517
|
+
messages.splice(toolMsgIdx, 1);
|
|
15518
|
+
}
|
|
15519
|
+
};
|
|
15520
|
+
const getProtectedStartIndex = (messages, protectedMessageCount) => {
|
|
15521
|
+
return Math.max(0, messages.length - protectedMessageCount);
|
|
15522
|
+
};
|
|
15523
|
+
const mergeConsecutiveAssistantMessages = (messages) => {
|
|
15524
|
+
for (let i = messages.length - 2; i >= 0; i--) {
|
|
15525
|
+
const current = messages[i];
|
|
15526
|
+
const next = messages[i + 1];
|
|
15527
|
+
if (current.role !== "assistant" || next.role !== "assistant") {
|
|
15528
|
+
continue;
|
|
15529
|
+
}
|
|
15530
|
+
const currentHasToolCalls = Array.isArray(current.content) && current.content.some((p) => p.type === "tool-call");
|
|
15531
|
+
const nextHasToolCalls = Array.isArray(next.content) && next.content.some((p) => p.type === "tool-call");
|
|
15532
|
+
if (currentHasToolCalls || nextHasToolCalls) {
|
|
15533
|
+
continue;
|
|
15534
|
+
}
|
|
15535
|
+
const currentTextParts = Array.isArray(current.content) ? current.content.filter((p) => p.type === "text") : [];
|
|
15536
|
+
const nextTextParts = Array.isArray(next.content) ? next.content.filter((p) => p.type === "text") : [];
|
|
15537
|
+
const mergedText = [...currentTextParts, ...nextTextParts].map((p) => p.text).filter(Boolean).join("\n\n");
|
|
15538
|
+
current.content = mergedText ? [{ type: "text", text: mergedText }] : [];
|
|
15539
|
+
messages.splice(i + 1, 1);
|
|
15540
|
+
}
|
|
15541
|
+
return messages.filter((msg) => {
|
|
15542
|
+
if (msg.role === "assistant" && Array.isArray(msg.content) && msg.content.length === 0) {
|
|
15543
|
+
return false;
|
|
15544
|
+
}
|
|
15545
|
+
return true;
|
|
15546
|
+
});
|
|
15547
|
+
};
|
|
15548
|
+
const truncateNonPowerToolResults = async (messages, protectedMessageCount = 10) => {
|
|
15549
|
+
const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
|
|
15550
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15551
|
+
const msg = messages[i];
|
|
15552
|
+
if (msg.role !== "tool") {
|
|
15553
|
+
continue;
|
|
15554
|
+
}
|
|
15555
|
+
for (let j = 0; j < msg.content.length; j++) {
|
|
15556
|
+
const part = msg.content[j];
|
|
15557
|
+
if (part.type !== "tool-result") {
|
|
15558
|
+
continue;
|
|
15559
|
+
}
|
|
15560
|
+
const [serverName] = extractServerNameToolName(part.toolName);
|
|
15561
|
+
if (isPowerTool(serverName)) {
|
|
15562
|
+
continue;
|
|
15563
|
+
}
|
|
15564
|
+
if (part.output.type !== "text" && part.output.type !== "error-text") {
|
|
15565
|
+
continue;
|
|
15566
|
+
}
|
|
15567
|
+
const outputText = part.output.value;
|
|
15568
|
+
if (!outputText) {
|
|
15569
|
+
continue;
|
|
15570
|
+
}
|
|
15571
|
+
const truncated = await truncateToolResult(
|
|
15572
|
+
outputText,
|
|
15573
|
+
20,
|
|
15574
|
+
2,
|
|
15575
|
+
2e3,
|
|
15576
|
+
false,
|
|
15577
|
+
"Output truncated due to compaction, re-execute the tool if full output is needed."
|
|
15578
|
+
);
|
|
15579
|
+
if (truncated !== outputText) {
|
|
15580
|
+
part.output = {
|
|
15581
|
+
type: part.output.type,
|
|
15582
|
+
value: truncated
|
|
15583
|
+
};
|
|
15584
|
+
}
|
|
15585
|
+
}
|
|
15586
|
+
}
|
|
15587
|
+
return messages;
|
|
15588
|
+
};
|
|
15589
|
+
const smartCompactMessages = async (messages, protectedMessageCount = 10) => {
|
|
15590
|
+
let result = cloneMessages(messages);
|
|
15591
|
+
result = removeErroredTools(result, protectedMessageCount);
|
|
15592
|
+
result = collapseFileEdits(result, protectedMessageCount);
|
|
15593
|
+
result = removeStaleFileReads(result, protectedMessageCount);
|
|
15594
|
+
result = compactFileReads(result, protectedMessageCount);
|
|
15595
|
+
result = removeObsoleteSearches(result, protectedMessageCount);
|
|
15596
|
+
result = compactSemanticSearches(result, protectedMessageCount);
|
|
15597
|
+
result = deduplicateBash(result, protectedMessageCount);
|
|
15598
|
+
result = redactFetchOutputs(result, protectedMessageCount);
|
|
15599
|
+
result = await truncateNonPowerToolResults(result, protectedMessageCount);
|
|
15600
|
+
result = mergeConsecutiveAssistantMessages(result);
|
|
15601
|
+
return result;
|
|
15602
|
+
};
|
|
15603
|
+
const removeErroredTools = (messages, protectedMessageCount = 10) => {
|
|
15604
|
+
const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
|
|
15605
|
+
for (let i = protectedStart - 1; i >= 0; i--) {
|
|
15606
|
+
if (i >= messages.length) {
|
|
15607
|
+
continue;
|
|
15608
|
+
}
|
|
15609
|
+
const msg = messages[i];
|
|
15610
|
+
if (msg.role !== "tool") {
|
|
15611
|
+
continue;
|
|
15612
|
+
}
|
|
15613
|
+
const toolInfos = getToolInfoFromToolMessage(msg);
|
|
15614
|
+
for (const info of toolInfos) {
|
|
15615
|
+
if (!isPowerTool(info.serverName)) {
|
|
15616
|
+
continue;
|
|
15617
|
+
}
|
|
15618
|
+
if (isErrorResult(info.output) || isNoOpResult(info.output)) {
|
|
15619
|
+
removeToolCallFromAssistant(messages, info.toolCallId);
|
|
15620
|
+
removeToolResult(messages, info.toolCallId);
|
|
15621
|
+
}
|
|
15622
|
+
}
|
|
15623
|
+
}
|
|
15624
|
+
return messages;
|
|
15625
|
+
};
|
|
15626
|
+
const collapseFileEdits = (messages, protectedMessageCount = 10) => {
|
|
15627
|
+
const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
|
|
15628
|
+
const fileEditGroups = /* @__PURE__ */ new Map();
|
|
15629
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15630
|
+
if (i >= messages.length) {
|
|
15631
|
+
break;
|
|
15632
|
+
}
|
|
15633
|
+
const msg = messages[i];
|
|
15634
|
+
if (msg.role !== "tool") {
|
|
15635
|
+
continue;
|
|
15636
|
+
}
|
|
15637
|
+
for (const part of msg.content) {
|
|
15638
|
+
if (part.type !== "tool-result") {
|
|
15639
|
+
continue;
|
|
15640
|
+
}
|
|
15641
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15642
|
+
if (!isPowerTool(serverName)) {
|
|
15643
|
+
continue;
|
|
15644
|
+
}
|
|
15645
|
+
if (toolName !== POWER_TOOL_FILE_EDIT && toolName !== POWER_TOOL_FILE_WRITE) {
|
|
15646
|
+
continue;
|
|
15647
|
+
}
|
|
15648
|
+
const callInfo = findAssistantToolCall(messages, part.toolCallId);
|
|
15649
|
+
if (!callInfo) {
|
|
15650
|
+
continue;
|
|
15651
|
+
}
|
|
15652
|
+
const filePath = extractFilePath(callInfo.input);
|
|
15653
|
+
if (!filePath) {
|
|
15654
|
+
continue;
|
|
15655
|
+
}
|
|
15656
|
+
if (!fileEditGroups.has(filePath)) {
|
|
15657
|
+
fileEditGroups.set(filePath, []);
|
|
15658
|
+
}
|
|
15659
|
+
fileEditGroups.get(filePath).push({
|
|
15660
|
+
assistantMessageId: callInfo.messageId,
|
|
15661
|
+
toolCallId: part.toolCallId
|
|
15662
|
+
});
|
|
15663
|
+
}
|
|
15664
|
+
}
|
|
15665
|
+
for (const [filePath, edits] of fileEditGroups) {
|
|
15666
|
+
if (edits.length === 0) {
|
|
15667
|
+
continue;
|
|
15668
|
+
}
|
|
15669
|
+
const lastEdit = edits[edits.length - 1];
|
|
15670
|
+
const syntheticMessage = {
|
|
15671
|
+
id: uuid.v4(),
|
|
15672
|
+
role: "assistant",
|
|
15673
|
+
content: [
|
|
15674
|
+
{
|
|
15675
|
+
type: "text",
|
|
15676
|
+
text: `<file-edited path="${filePath}">File was edited. Read the content again if you need to work on it.</file-edited>`
|
|
15677
|
+
}
|
|
15678
|
+
]
|
|
15679
|
+
};
|
|
15680
|
+
const assistantIndex = messages.findIndex((m) => m.id === lastEdit.assistantMessageId);
|
|
15681
|
+
const insertIndex = assistantIndex !== -1 ? assistantIndex + 1 : 0;
|
|
15682
|
+
messages.splice(insertIndex, 0, syntheticMessage);
|
|
15683
|
+
for (const edit of edits) {
|
|
15684
|
+
removeToolCallFromAssistant(messages, edit.toolCallId);
|
|
15685
|
+
removeToolResult(messages, edit.toolCallId);
|
|
15686
|
+
}
|
|
15687
|
+
}
|
|
15688
|
+
return messages;
|
|
15689
|
+
};
|
|
15690
|
+
const removeStaleFileReads = (messages, protectedMessageCount = 10) => {
|
|
15691
|
+
const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
|
|
15692
|
+
const editedFilePaths = /* @__PURE__ */ new Set();
|
|
15693
|
+
for (let i = 0; i < messages.length; i++) {
|
|
15694
|
+
const msg = messages[i];
|
|
15695
|
+
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
15696
|
+
for (const part of msg.content) {
|
|
15697
|
+
if (part.type === "text") {
|
|
15698
|
+
const match = part.text.match(/<file-edited path="([^"]+)">/);
|
|
15699
|
+
if (match) {
|
|
15700
|
+
editedFilePaths.add(match[1]);
|
|
15701
|
+
}
|
|
15702
|
+
}
|
|
15703
|
+
}
|
|
15704
|
+
continue;
|
|
15705
|
+
}
|
|
15706
|
+
if (msg.role !== "tool") {
|
|
15707
|
+
continue;
|
|
15708
|
+
}
|
|
15709
|
+
for (const part of msg.content) {
|
|
15710
|
+
if (part.type !== "tool-result") {
|
|
15711
|
+
continue;
|
|
15712
|
+
}
|
|
15713
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15714
|
+
if (!isPowerTool(serverName)) {
|
|
15715
|
+
continue;
|
|
15716
|
+
}
|
|
15717
|
+
if (toolName !== POWER_TOOL_FILE_EDIT && toolName !== POWER_TOOL_FILE_WRITE) {
|
|
15718
|
+
continue;
|
|
15719
|
+
}
|
|
15720
|
+
const callInfo = findAssistantToolCall(messages, part.toolCallId);
|
|
15721
|
+
if (callInfo) {
|
|
15722
|
+
const filePath = extractFilePath(callInfo.input);
|
|
15723
|
+
if (filePath) {
|
|
15724
|
+
editedFilePaths.add(filePath);
|
|
15725
|
+
}
|
|
15726
|
+
}
|
|
15727
|
+
}
|
|
15728
|
+
}
|
|
15729
|
+
const protectedReadFilePaths = /* @__PURE__ */ new Set();
|
|
15730
|
+
for (let i = protectedStart; i < messages.length; i++) {
|
|
15731
|
+
const msg = messages[i];
|
|
15732
|
+
if (msg.role !== "tool") {
|
|
15733
|
+
continue;
|
|
15734
|
+
}
|
|
15735
|
+
for (const part of msg.content) {
|
|
15736
|
+
if (part.type !== "tool-result") {
|
|
15737
|
+
continue;
|
|
15738
|
+
}
|
|
15739
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15740
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_FILE_READ) {
|
|
15741
|
+
continue;
|
|
15742
|
+
}
|
|
15743
|
+
const callInfo = findAssistantToolCall(messages, part.toolCallId);
|
|
15744
|
+
if (callInfo) {
|
|
15745
|
+
const filePath = extractFilePath(callInfo.input);
|
|
15746
|
+
if (filePath) {
|
|
15747
|
+
protectedReadFilePaths.add(filePath);
|
|
15748
|
+
}
|
|
15749
|
+
}
|
|
15750
|
+
}
|
|
15751
|
+
}
|
|
15752
|
+
const readFileGroups = /* @__PURE__ */ new Map();
|
|
15753
|
+
for (let i = protectedStart - 1; i >= 0; i--) {
|
|
15754
|
+
if (i >= messages.length) {
|
|
15755
|
+
continue;
|
|
15756
|
+
}
|
|
15757
|
+
const msg = messages[i];
|
|
15758
|
+
if (msg.role !== "tool") {
|
|
15759
|
+
continue;
|
|
15760
|
+
}
|
|
15761
|
+
for (const part of msg.content) {
|
|
15762
|
+
if (part.type !== "tool-result") {
|
|
15763
|
+
continue;
|
|
15764
|
+
}
|
|
15765
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15766
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_FILE_READ) {
|
|
15767
|
+
continue;
|
|
15768
|
+
}
|
|
15769
|
+
const callInfo = findAssistantToolCall(messages, part.toolCallId);
|
|
15770
|
+
if (!callInfo) {
|
|
15771
|
+
continue;
|
|
15772
|
+
}
|
|
15773
|
+
const filePath = extractFilePath(callInfo.input);
|
|
15774
|
+
if (!filePath) {
|
|
15775
|
+
continue;
|
|
15776
|
+
}
|
|
15777
|
+
if (editedFilePaths.has(filePath) || protectedReadFilePaths.has(filePath)) {
|
|
15778
|
+
removeToolCallFromAssistant(messages, part.toolCallId);
|
|
15779
|
+
removeToolResult(messages, part.toolCallId);
|
|
15780
|
+
continue;
|
|
15781
|
+
}
|
|
15782
|
+
if (!readFileGroups.has(filePath)) {
|
|
15783
|
+
readFileGroups.set(filePath, []);
|
|
15784
|
+
}
|
|
15785
|
+
readFileGroups.get(filePath).push({
|
|
15786
|
+
messageIndex: i,
|
|
15787
|
+
toolCallId: part.toolCallId
|
|
15788
|
+
});
|
|
15789
|
+
}
|
|
15790
|
+
}
|
|
15791
|
+
for (const [, reads] of readFileGroups) {
|
|
15792
|
+
if (reads.length <= 1) {
|
|
15793
|
+
continue;
|
|
15794
|
+
}
|
|
15795
|
+
const toRemove = reads.slice(0, -1);
|
|
15796
|
+
for (const read of toRemove) {
|
|
15797
|
+
removeToolCallFromAssistant(messages, read.toolCallId);
|
|
15798
|
+
removeToolResult(messages, read.toolCallId);
|
|
15799
|
+
}
|
|
15800
|
+
}
|
|
15801
|
+
return messages;
|
|
15802
|
+
};
|
|
15803
|
+
const compactFileReads = (messages, protectedMessageCount = 10) => {
|
|
15804
|
+
const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
|
|
15805
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15806
|
+
if (i >= messages.length) {
|
|
15807
|
+
break;
|
|
15808
|
+
}
|
|
15809
|
+
const msg = messages[i];
|
|
15810
|
+
if (msg.role !== "tool") {
|
|
15811
|
+
continue;
|
|
15812
|
+
}
|
|
15813
|
+
for (let j = 0; j < msg.content.length; j++) {
|
|
15814
|
+
const part = msg.content[j];
|
|
15815
|
+
if (part.type !== "tool-result") {
|
|
15816
|
+
continue;
|
|
15817
|
+
}
|
|
15818
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15819
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_FILE_READ) {
|
|
15820
|
+
continue;
|
|
15821
|
+
}
|
|
15822
|
+
if (part.output.type !== "text") {
|
|
15823
|
+
continue;
|
|
15824
|
+
}
|
|
15825
|
+
const lines = part.output.value.split("\n");
|
|
15826
|
+
if (lines.length > 50) {
|
|
15827
|
+
part.output = {
|
|
15828
|
+
type: "text",
|
|
15829
|
+
value: lines.slice(0, 50).join("\n") + "\n<truncated due to compaction, read the file again if full content is needed>"
|
|
15830
|
+
};
|
|
15831
|
+
}
|
|
15832
|
+
}
|
|
15833
|
+
}
|
|
15834
|
+
return messages;
|
|
15835
|
+
};
|
|
15836
|
+
const removeObsoleteSearches = (messages, protectedMessageCount = 10) => {
|
|
15837
|
+
const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
|
|
15838
|
+
const fileModificationPositions = [];
|
|
15839
|
+
for (let i = 0; i < messages.length; i++) {
|
|
15840
|
+
const msg = messages[i];
|
|
15841
|
+
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
15842
|
+
for (const part of msg.content) {
|
|
15843
|
+
if (part.type === "text" && part.text.includes("<file-edited")) {
|
|
15844
|
+
fileModificationPositions.push(i);
|
|
15845
|
+
break;
|
|
15846
|
+
}
|
|
15847
|
+
}
|
|
15848
|
+
continue;
|
|
15849
|
+
}
|
|
15850
|
+
if (msg.role !== "tool") {
|
|
15851
|
+
continue;
|
|
15852
|
+
}
|
|
15853
|
+
for (const part of msg.content) {
|
|
15854
|
+
if (part.type !== "tool-result") {
|
|
15855
|
+
continue;
|
|
15856
|
+
}
|
|
15857
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15858
|
+
if (!isPowerTool(serverName)) {
|
|
15859
|
+
continue;
|
|
15860
|
+
}
|
|
15861
|
+
if (toolName === POWER_TOOL_FILE_EDIT || toolName === POWER_TOOL_FILE_WRITE) {
|
|
15862
|
+
fileModificationPositions.push(i);
|
|
15863
|
+
}
|
|
15864
|
+
}
|
|
15865
|
+
}
|
|
15866
|
+
const hasFileModifications = fileModificationPositions.length > 0;
|
|
15867
|
+
if (!hasFileModifications) {
|
|
15868
|
+
return messages;
|
|
15869
|
+
}
|
|
15870
|
+
for (let i = protectedStart - 1; i >= 0; i--) {
|
|
15871
|
+
if (i >= messages.length) {
|
|
15872
|
+
continue;
|
|
15873
|
+
}
|
|
15874
|
+
const msg = messages[i];
|
|
15875
|
+
if (msg.role !== "tool") {
|
|
15876
|
+
continue;
|
|
15877
|
+
}
|
|
15878
|
+
for (const part of msg.content) {
|
|
15879
|
+
if (part.type !== "tool-result") {
|
|
15880
|
+
continue;
|
|
15881
|
+
}
|
|
15882
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15883
|
+
if (!isPowerTool(serverName)) {
|
|
15884
|
+
continue;
|
|
15885
|
+
}
|
|
15886
|
+
if (toolName !== POWER_TOOL_GLOB && toolName !== POWER_TOOL_GREP) {
|
|
15887
|
+
continue;
|
|
15888
|
+
}
|
|
15889
|
+
const hasLaterModification = fileModificationPositions.some((pos) => pos > i);
|
|
15890
|
+
if (hasLaterModification) {
|
|
15891
|
+
removeToolCallFromAssistant(messages, part.toolCallId);
|
|
15892
|
+
removeToolResult(messages, part.toolCallId);
|
|
15893
|
+
}
|
|
15894
|
+
}
|
|
15895
|
+
}
|
|
15896
|
+
return messages;
|
|
15897
|
+
};
|
|
15898
|
+
const compactSemanticSearches = (messages, protectedMessageCount = 10) => {
|
|
15899
|
+
const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
|
|
15900
|
+
const searchIndices = [];
|
|
15901
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15902
|
+
if (i >= messages.length) {
|
|
15903
|
+
break;
|
|
15904
|
+
}
|
|
15905
|
+
const msg = messages[i];
|
|
15906
|
+
if (msg.role !== "tool") {
|
|
15907
|
+
continue;
|
|
15908
|
+
}
|
|
15909
|
+
for (let j = 0; j < msg.content.length; j++) {
|
|
15910
|
+
const part = msg.content[j];
|
|
15911
|
+
if (part.type !== "tool-result") {
|
|
15912
|
+
continue;
|
|
15913
|
+
}
|
|
15914
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15915
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_SEMANTIC_SEARCH) {
|
|
15916
|
+
continue;
|
|
15917
|
+
}
|
|
15918
|
+
searchIndices.push({ messageIndex: i, partIndex: j, toolCallId: part.toolCallId });
|
|
15919
|
+
}
|
|
15920
|
+
}
|
|
15921
|
+
if (searchIndices.length <= 1) {
|
|
15922
|
+
return messages;
|
|
15923
|
+
}
|
|
15924
|
+
const toRemove = searchIndices.slice(0, -1);
|
|
15925
|
+
const toKeep = searchIndices[searchIndices.length - 1];
|
|
15926
|
+
for (const search2 of toRemove) {
|
|
15927
|
+
removeToolCallFromAssistant(messages, search2.toolCallId);
|
|
15928
|
+
removeToolResult(messages, search2.toolCallId);
|
|
15929
|
+
}
|
|
15930
|
+
const keptToolIdx = messages.findIndex((m) => m.role === "tool" && m.content.some((p) => p.type === "tool-result" && p.toolCallId === toKeep.toolCallId));
|
|
15931
|
+
if (keptToolIdx !== -1) {
|
|
15932
|
+
const keptMsg = messages[keptToolIdx];
|
|
15933
|
+
const keptPartIdx = keptMsg.content.findIndex((p) => p.type === "tool-result" && p.toolCallId === toKeep.toolCallId);
|
|
15934
|
+
const keptPart = keptMsg.content[keptPartIdx];
|
|
15935
|
+
if (keptPart && keptPart.output.type === "text") {
|
|
15936
|
+
const lines = keptPart.output.value.split("\n");
|
|
15937
|
+
if (lines.length > 50) {
|
|
15938
|
+
keptPart.output = {
|
|
15939
|
+
type: "text",
|
|
15940
|
+
value: lines.slice(0, 50).join("\n") + "\n<truncated due to compaction, run again if full output is needed>"
|
|
15941
|
+
};
|
|
15942
|
+
}
|
|
15943
|
+
}
|
|
15944
|
+
}
|
|
15945
|
+
return messages;
|
|
15946
|
+
};
|
|
15947
|
+
const deduplicateBash = (messages, protectedMessageCount = 10) => {
|
|
15948
|
+
const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
|
|
15949
|
+
const bashCommands = /* @__PURE__ */ new Map();
|
|
15950
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15951
|
+
const msg = messages[i];
|
|
15952
|
+
if (msg.role !== "tool") {
|
|
15953
|
+
continue;
|
|
15954
|
+
}
|
|
15955
|
+
for (const part of msg.content) {
|
|
15956
|
+
if (part.type !== "tool-result") {
|
|
15957
|
+
continue;
|
|
15958
|
+
}
|
|
15959
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15960
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_BASH) {
|
|
15961
|
+
continue;
|
|
15962
|
+
}
|
|
15963
|
+
const callInfo = findAssistantToolCall(messages, part.toolCallId);
|
|
15964
|
+
if (!callInfo) {
|
|
15965
|
+
continue;
|
|
15966
|
+
}
|
|
15967
|
+
const command = typeof callInfo.input?.command === "string" ? callInfo.input.command.trim() : null;
|
|
15968
|
+
if (!command) {
|
|
15969
|
+
continue;
|
|
15970
|
+
}
|
|
15971
|
+
if (!bashCommands.has(command)) {
|
|
15972
|
+
bashCommands.set(command, []);
|
|
15973
|
+
}
|
|
15974
|
+
bashCommands.get(command).push({
|
|
15975
|
+
messageIndex: i,
|
|
15976
|
+
toolCallId: part.toolCallId
|
|
15977
|
+
});
|
|
15978
|
+
}
|
|
15979
|
+
}
|
|
15980
|
+
for (const [, occurrences] of bashCommands) {
|
|
15981
|
+
if (occurrences.length <= 1) {
|
|
15982
|
+
continue;
|
|
15983
|
+
}
|
|
15984
|
+
const toRemove = occurrences.slice(0, -1);
|
|
15985
|
+
for (const occ of toRemove) {
|
|
15986
|
+
removeToolCallFromAssistant(messages, occ.toolCallId);
|
|
15987
|
+
removeToolResult(messages, occ.toolCallId);
|
|
15988
|
+
}
|
|
15989
|
+
}
|
|
15990
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15991
|
+
if (i >= messages.length) {
|
|
15992
|
+
break;
|
|
15993
|
+
}
|
|
15994
|
+
const msg = messages[i];
|
|
15995
|
+
if (msg.role !== "tool") {
|
|
15996
|
+
continue;
|
|
15997
|
+
}
|
|
15998
|
+
for (let j = 0; j < msg.content.length; j++) {
|
|
15999
|
+
const part = msg.content[j];
|
|
16000
|
+
if (part.type !== "tool-result") {
|
|
16001
|
+
continue;
|
|
16002
|
+
}
|
|
16003
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
16004
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_BASH) {
|
|
16005
|
+
continue;
|
|
16006
|
+
}
|
|
16007
|
+
if (part.output.type !== "text") {
|
|
16008
|
+
continue;
|
|
16009
|
+
}
|
|
16010
|
+
try {
|
|
16011
|
+
const parsed = JSON.parse(part.output.value);
|
|
16012
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
16013
|
+
const stdout = typeof parsed.stdout === "string" ? parsed.stdout : "";
|
|
16014
|
+
const stderr = typeof parsed.stderr === "string" ? parsed.stderr : "";
|
|
16015
|
+
const redactionMessage = "<output redacted due to compaction, run again if output is needed>";
|
|
16016
|
+
let modified = false;
|
|
16017
|
+
if (stdout.length > 30) {
|
|
16018
|
+
parsed.stdout = redactionMessage;
|
|
16019
|
+
modified = true;
|
|
16020
|
+
}
|
|
16021
|
+
if (stderr.length > 30) {
|
|
16022
|
+
parsed.stderr = redactionMessage;
|
|
16023
|
+
modified = true;
|
|
16024
|
+
}
|
|
16025
|
+
if (modified) {
|
|
16026
|
+
part.output = {
|
|
16027
|
+
type: "text",
|
|
16028
|
+
value: JSON.stringify(parsed)
|
|
16029
|
+
};
|
|
16030
|
+
}
|
|
16031
|
+
}
|
|
16032
|
+
} catch {
|
|
16033
|
+
}
|
|
16034
|
+
}
|
|
16035
|
+
}
|
|
16036
|
+
return messages;
|
|
16037
|
+
};
|
|
16038
|
+
const redactFetchOutputs = (messages, protectedMessageCount = 10) => {
|
|
16039
|
+
const protectedStart = getProtectedStartIndex(messages, protectedMessageCount);
|
|
16040
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
16041
|
+
const msg = messages[i];
|
|
16042
|
+
if (msg.role !== "tool") {
|
|
16043
|
+
continue;
|
|
16044
|
+
}
|
|
16045
|
+
for (let j = 0; j < msg.content.length; j++) {
|
|
16046
|
+
const part = msg.content[j];
|
|
16047
|
+
if (part.type !== "tool-result") {
|
|
16048
|
+
continue;
|
|
16049
|
+
}
|
|
16050
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
16051
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_FETCH) {
|
|
16052
|
+
continue;
|
|
16053
|
+
}
|
|
16054
|
+
const outputText = getToolOutputText(part.output);
|
|
16055
|
+
if (outputText.length > 0) {
|
|
16056
|
+
part.output = {
|
|
16057
|
+
type: "text",
|
|
16058
|
+
value: "<content redacted due to compaction, fetch again if content is needed>"
|
|
16059
|
+
};
|
|
16060
|
+
}
|
|
16061
|
+
}
|
|
14612
16062
|
}
|
|
14613
|
-
|
|
16063
|
+
return messages;
|
|
16064
|
+
};
|
|
14614
16065
|
const INTERNAL_TASK_ID = "internal";
|
|
14615
16066
|
const RESPONSE_CHUNK_FLUSH_INTERVAL_MS = 10;
|
|
14616
16067
|
const EMPTY_TASK_DATA = {
|
|
@@ -14622,7 +16073,6 @@ const EMPTY_TASK_DATA = {
|
|
|
14622
16073
|
agentTotalCost: 0,
|
|
14623
16074
|
mainModel: "",
|
|
14624
16075
|
currentMode: "agent",
|
|
14625
|
-
contextCompactingThreshold: 0,
|
|
14626
16076
|
weakModelLocked: false,
|
|
14627
16077
|
parentId: null,
|
|
14628
16078
|
lastAgentProviderMetadata: void 0
|
|
@@ -15117,7 +16567,7 @@ class Task {
|
|
|
15117
16567
|
isPromptRunning() {
|
|
15118
16568
|
return !!this.currentPromptContext || this.agent.isRunning() || this.isCompacting;
|
|
15119
16569
|
}
|
|
15120
|
-
async runPrompt(prompt, mode = this.task.currentMode || "agent", addToInputHistory = true, userMessageId = uuid.v4(), sendNotification = true) {
|
|
16570
|
+
async runPrompt(prompt, mode = this.task.currentMode || "agent", addToInputHistory = true, userMessageId = uuid.v4(), sendNotification = true, images) {
|
|
15121
16571
|
if (this.currentQuestion) {
|
|
15122
16572
|
if (await this.answerQuestion("n", prompt)) {
|
|
15123
16573
|
logger.debug("Processed by the answerQuestion function.");
|
|
@@ -15155,7 +16605,7 @@ class Task {
|
|
|
15155
16605
|
if (addToInputHistory) {
|
|
15156
16606
|
await this.project.addToInputHistory(prompt);
|
|
15157
16607
|
}
|
|
15158
|
-
this.addUserMessage(userMessageId, prompt);
|
|
16608
|
+
this.addUserMessage(userMessageId, prompt, promptContext, images);
|
|
15159
16609
|
this.addLogMessage("loading");
|
|
15160
16610
|
this.telemetryManager.captureRunPrompt(mode);
|
|
15161
16611
|
let responses = [];
|
|
@@ -15165,7 +16615,7 @@ class Task {
|
|
|
15165
16615
|
if (!profile) {
|
|
15166
16616
|
throw new Error("No active Agent profile found");
|
|
15167
16617
|
}
|
|
15168
|
-
responses = await this.runPromptInAgent(profile, mode, prompt, promptContext, void 0, void 0, void 0, true, sendNotification);
|
|
16618
|
+
responses = await this.runPromptInAgent(profile, mode, prompt, promptContext, void 0, void 0, void 0, true, sendNotification, images);
|
|
15169
16619
|
} else {
|
|
15170
16620
|
responses = await this.runPromptInAider(mode, prompt, promptContext, sendNotification);
|
|
15171
16621
|
}
|
|
@@ -15187,7 +16637,8 @@ class Task {
|
|
|
15187
16637
|
id: promptContext.id,
|
|
15188
16638
|
role: MessageRole.User,
|
|
15189
16639
|
content: prompt,
|
|
15190
|
-
promptContext
|
|
16640
|
+
promptContext,
|
|
16641
|
+
timestamp: Date.now()
|
|
15191
16642
|
});
|
|
15192
16643
|
this.addUserMessage(promptContext.id, prompt);
|
|
15193
16644
|
await this.saveTask({
|
|
@@ -15220,7 +16671,8 @@ class Task {
|
|
|
15220
16671
|
id: promptContext.id,
|
|
15221
16672
|
role: MessageRole.User,
|
|
15222
16673
|
content: prompt,
|
|
15223
|
-
promptContext
|
|
16674
|
+
promptContext,
|
|
16675
|
+
timestamp: Date.now()
|
|
15224
16676
|
});
|
|
15225
16677
|
let messages = this.contextManager.toConnectorMessages();
|
|
15226
16678
|
let files = this.contextManager.getContextFiles();
|
|
@@ -15239,8 +16691,9 @@ class Task {
|
|
|
15239
16691
|
promptContext = extensionResult.promptContext;
|
|
15240
16692
|
messages = extensionResult.messages;
|
|
15241
16693
|
files = extensionResult.files;
|
|
16694
|
+
const effectiveAutonomyMode = extensionResult.autonomyMode ?? this.task.autonomyMode ?? DEFAULT_AUTONOMY_MODE;
|
|
15242
16695
|
let responses = await this.sendPromptToAider(prompt, promptContext, mode, messages, files, {
|
|
15243
|
-
autoApprove:
|
|
16696
|
+
autoApprove: effectiveAutonomyMode === AutonomyMode.Autonomous,
|
|
15244
16697
|
denyCommands: extensionResult.denyCommands
|
|
15245
16698
|
});
|
|
15246
16699
|
logger.debug("Responses:", { responses });
|
|
@@ -15277,7 +16730,7 @@ class Task {
|
|
|
15277
16730
|
}
|
|
15278
16731
|
return responses;
|
|
15279
16732
|
}
|
|
15280
|
-
async runPromptInAgent(profile, mode, prompt, promptContext = { id: uuid.v4() }, contextMessages, contextFiles, systemPrompt, waitForCurrentAgentToFinish = true, sendNotification = true) {
|
|
16733
|
+
async runPromptInAgent(profile, mode, prompt, promptContext = { id: uuid.v4() }, contextMessages, contextFiles, systemPrompt, waitForCurrentAgentToFinish = true, sendNotification = true, images) {
|
|
15281
16734
|
if (waitForCurrentAgentToFinish) {
|
|
15282
16735
|
await this.waitForCurrentAgentToFinish();
|
|
15283
16736
|
}
|
|
@@ -15288,7 +16741,19 @@ class Task {
|
|
|
15288
16741
|
provider: this.task.provider || profile.provider,
|
|
15289
16742
|
model: this.task.model || profile.model
|
|
15290
16743
|
});
|
|
15291
|
-
const agentMessages = await this.agent.runAgent(
|
|
16744
|
+
const agentMessages = await this.agent.runAgent(
|
|
16745
|
+
this,
|
|
16746
|
+
profile,
|
|
16747
|
+
prompt,
|
|
16748
|
+
mode,
|
|
16749
|
+
promptContext,
|
|
16750
|
+
contextMessages,
|
|
16751
|
+
contextFiles,
|
|
16752
|
+
systemPrompt,
|
|
16753
|
+
true,
|
|
16754
|
+
void 0,
|
|
16755
|
+
images
|
|
16756
|
+
);
|
|
15292
16757
|
if (agentMessages.length > 0) {
|
|
15293
16758
|
this.contextManager.toConnectorMessages(agentMessages).forEach((message) => {
|
|
15294
16759
|
this.sendAddMessage(message.role, message.content, false);
|
|
@@ -15613,7 +17078,8 @@ ${contentText}</agent-response>`;
|
|
|
15613
17078
|
diff: message.diff,
|
|
15614
17079
|
usageReport,
|
|
15615
17080
|
sequenceNumber: message.sequenceNumber,
|
|
15616
|
-
promptContext: message.promptContext
|
|
17081
|
+
promptContext: message.promptContext,
|
|
17082
|
+
timestamp: Date.now()
|
|
15617
17083
|
};
|
|
15618
17084
|
const extensionResult = await this.extensionManager.dispatchEvent("onResponseCompleted", { response: data }, this.project, this);
|
|
15619
17085
|
data = extensionResult.response;
|
|
@@ -15680,14 +17146,20 @@ ${contentText}</agent-response>`;
|
|
|
15680
17146
|
if (!determinedAnswer) {
|
|
15681
17147
|
determinedAnswer = normalizedAnswer === "a" || normalizedAnswer === "y" ? "y" : "n";
|
|
15682
17148
|
}
|
|
15683
|
-
if (
|
|
15684
|
-
logger.debug('Storing answer for question due to "
|
|
17149
|
+
if (normalizedAnswer === "a") {
|
|
17150
|
+
logger.debug('Storing answer for question due to "a" (Always) input:', {
|
|
17151
|
+
baseDir: this.project.baseDir,
|
|
17152
|
+
questionKey: this.getQuestionKey(this.currentQuestion),
|
|
17153
|
+
rawInput: answer
|
|
17154
|
+
});
|
|
17155
|
+
this.storedQuestionAnswers.set(this.getQuestionKey(this.currentQuestion), "y");
|
|
17156
|
+
} else if (normalizedAnswer === "d") {
|
|
17157
|
+
logger.debug(`Storing answer for question due to "d" (Don't ask again) input:`, {
|
|
15685
17158
|
baseDir: this.project.baseDir,
|
|
15686
17159
|
questionKey: this.getQuestionKey(this.currentQuestion),
|
|
15687
|
-
rawInput: answer
|
|
15688
|
-
determinedAndStoredAnswer: determinedAnswer
|
|
17160
|
+
rawInput: answer
|
|
15689
17161
|
});
|
|
15690
|
-
this.storedQuestionAnswers.set(this.getQuestionKey(this.currentQuestion),
|
|
17162
|
+
this.storedQuestionAnswers.set(this.getQuestionKey(this.currentQuestion), "n");
|
|
15691
17163
|
}
|
|
15692
17164
|
const questionToAnswer = this.currentQuestion;
|
|
15693
17165
|
if (!this.currentQuestion.internal) {
|
|
@@ -15850,11 +17322,21 @@ ${contentText}</agent-response>`;
|
|
|
15850
17322
|
void this.updateContextInfo(true, true);
|
|
15851
17323
|
}
|
|
15852
17324
|
async askQuestion(questionData, awaitAnswer = true) {
|
|
15853
|
-
|
|
17325
|
+
let storedAnswer = this.storedQuestionAnswers.get(this.getQuestionKey(questionData));
|
|
17326
|
+
const extensionResult = await this.extensionManager.dispatchEvent(
|
|
17327
|
+
"onQuestionAsked",
|
|
17328
|
+
{
|
|
17329
|
+
question: questionData,
|
|
17330
|
+
storedAnswer
|
|
17331
|
+
},
|
|
17332
|
+
this.project,
|
|
17333
|
+
this
|
|
17334
|
+
);
|
|
15854
17335
|
if (extensionResult.answer) {
|
|
15855
17336
|
logger.info("Question answered by extension", {
|
|
15856
17337
|
question: questionData.text,
|
|
15857
|
-
answer: extensionResult.answer
|
|
17338
|
+
answer: extensionResult.answer,
|
|
17339
|
+
storedAnswer
|
|
15858
17340
|
});
|
|
15859
17341
|
return [extensionResult.answer, void 0];
|
|
15860
17342
|
}
|
|
@@ -15864,7 +17346,7 @@ ${contentText}</agent-response>`;
|
|
|
15864
17346
|
this.currentQuestionResolves.push(resolve);
|
|
15865
17347
|
});
|
|
15866
17348
|
}
|
|
15867
|
-
|
|
17349
|
+
storedAnswer = this.storedQuestionAnswers.get(this.getQuestionKey(questionData));
|
|
15868
17350
|
if (questionData.isGroupQuestion && !questionData.answers) {
|
|
15869
17351
|
questionData.answers = [
|
|
15870
17352
|
{ text: "(Y)es", shortkey: "y" },
|
|
@@ -16205,7 +17687,8 @@ ${contentText}</agent-response>`;
|
|
|
16205
17687
|
message,
|
|
16206
17688
|
finished,
|
|
16207
17689
|
promptContext,
|
|
16208
|
-
actionIds
|
|
17690
|
+
actionIds,
|
|
17691
|
+
timestamp: Date.now()
|
|
16209
17692
|
};
|
|
16210
17693
|
this.eventManager.sendLog(data);
|
|
16211
17694
|
}
|
|
@@ -16325,7 +17808,8 @@ ${contentText}</agent-response>`;
|
|
|
16325
17808
|
sendUserMessage(data) {
|
|
16326
17809
|
logger.debug("Sending user message:", {
|
|
16327
17810
|
baseDir: this.project.baseDir,
|
|
16328
|
-
content: data.content
|
|
17811
|
+
content: data.content?.substring(0, 100),
|
|
17812
|
+
hasImages: (data.images?.length ?? 0) > 0
|
|
16329
17813
|
});
|
|
16330
17814
|
this.eventManager.sendUserMessage(data);
|
|
16331
17815
|
}
|
|
@@ -16342,18 +17826,20 @@ ${contentText}</agent-response>`;
|
|
|
16342
17826
|
});
|
|
16343
17827
|
this.eventManager.sendTool(data);
|
|
16344
17828
|
}
|
|
16345
|
-
async clearContext(addToHistory = false, updateContextInfo = true) {
|
|
17829
|
+
async clearContext(addToHistory = false, updateContextInfo = true, updateTaskState = true, createSnapshot = true) {
|
|
16346
17830
|
logger.info("Clearing context:", {
|
|
16347
17831
|
baseDir: this.project.baseDir,
|
|
16348
17832
|
addToHistory,
|
|
16349
17833
|
updateContextInfo
|
|
16350
17834
|
});
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
17835
|
+
if (updateTaskState) {
|
|
17836
|
+
await this.updateTask({
|
|
17837
|
+
state: DefaultTaskState.Todo,
|
|
17838
|
+
metadata: void 0,
|
|
17839
|
+
lastAgentProviderMetadata: null
|
|
17840
|
+
});
|
|
17841
|
+
}
|
|
17842
|
+
this.contextManager.clearMessages(true, createSnapshot);
|
|
16357
17843
|
await this.runCommand("clear", addToHistory);
|
|
16358
17844
|
this.eventManager.sendClearTask(this.project.baseDir, this.taskId, true, false);
|
|
16359
17845
|
if (updateContextInfo) {
|
|
@@ -16369,6 +17855,29 @@ ${contentText}</agent-response>`;
|
|
|
16369
17855
|
this.contextManager.clearMessages();
|
|
16370
17856
|
await this.contextManager.save();
|
|
16371
17857
|
}
|
|
17858
|
+
sendContextInfoUpdated() {
|
|
17859
|
+
this.eventManager.sendContextInfoUpdated({
|
|
17860
|
+
baseDir: this.project.baseDir,
|
|
17861
|
+
taskId: this.taskId,
|
|
17862
|
+
canUndoContextChange: this.contextManager.hasUndoSnapshot()
|
|
17863
|
+
});
|
|
17864
|
+
}
|
|
17865
|
+
async undoContextChange() {
|
|
17866
|
+
const snapshot = this.contextManager.undoContextChange();
|
|
17867
|
+
if (!snapshot) {
|
|
17868
|
+
logger.debug("No undo snapshot available", { taskId: this.taskId });
|
|
17869
|
+
return false;
|
|
17870
|
+
}
|
|
17871
|
+
logger.info("Undoing context change", {
|
|
17872
|
+
baseDir: this.project.baseDir,
|
|
17873
|
+
taskId: this.taskId,
|
|
17874
|
+
messagesCount: snapshot.length
|
|
17875
|
+
});
|
|
17876
|
+
await this.contextManager.loadMessages(snapshot);
|
|
17877
|
+
await this.updateContextInfo();
|
|
17878
|
+
this.sendContextInfoUpdated();
|
|
17879
|
+
return true;
|
|
17880
|
+
}
|
|
16372
17881
|
/**
|
|
16373
17882
|
* Load context messages into the task context and send them to the UI.
|
|
16374
17883
|
* This is used for loading pre-authored context (e.g., from extensions).
|
|
@@ -16382,6 +17891,12 @@ ${contentText}</agent-response>`;
|
|
|
16382
17891
|
await this.contextManager.loadMessages(messages);
|
|
16383
17892
|
}
|
|
16384
17893
|
async interruptResponse(interruptId) {
|
|
17894
|
+
const extensionResult = await this.extensionManager.dispatchEvent("onInterrupted", { interruptId }, this.project, this);
|
|
17895
|
+
if (extensionResult.blocked) {
|
|
17896
|
+
logger.debug("Interrupt blocked by extension", { interruptId });
|
|
17897
|
+
return;
|
|
17898
|
+
}
|
|
17899
|
+
interruptId = extensionResult.interruptId;
|
|
16385
17900
|
if (interruptId) {
|
|
16386
17901
|
const subagentAbortController = this.subagentAbortControllers[interruptId];
|
|
16387
17902
|
if (subagentAbortController) {
|
|
@@ -16525,7 +18040,8 @@ ${contentText}</agent-response>`;
|
|
|
16525
18040
|
response,
|
|
16526
18041
|
usageReport,
|
|
16527
18042
|
promptContext,
|
|
16528
|
-
finished
|
|
18043
|
+
finished,
|
|
18044
|
+
timestamp: Date.now()
|
|
16529
18045
|
};
|
|
16530
18046
|
if (response && usageReport && saveToDb) {
|
|
16531
18047
|
this.dataManager.saveMessage(id, "tool", this.project.baseDir, usageReport.model, usageReport, {
|
|
@@ -16554,7 +18070,7 @@ ${contentText}</agent-response>`;
|
|
|
16554
18070
|
this.eventManager.sendTaskUpdated(this.task);
|
|
16555
18071
|
}
|
|
16556
18072
|
}
|
|
16557
|
-
addUserMessage(id, content, promptContext) {
|
|
18073
|
+
addUserMessage(id, content, promptContext, images) {
|
|
16558
18074
|
logger.info("Adding user message:", {
|
|
16559
18075
|
baseDir: this.project.baseDir,
|
|
16560
18076
|
content: content.substring(0, 100)
|
|
@@ -16565,7 +18081,9 @@ ${contentText}</agent-response>`;
|
|
|
16565
18081
|
baseDir: this.project.baseDir,
|
|
16566
18082
|
taskId: this.taskId,
|
|
16567
18083
|
content,
|
|
16568
|
-
|
|
18084
|
+
images,
|
|
18085
|
+
promptContext,
|
|
18086
|
+
timestamp: Date.now()
|
|
16569
18087
|
};
|
|
16570
18088
|
this.eventManager.sendUserMessage(data);
|
|
16571
18089
|
}
|
|
@@ -16597,12 +18115,13 @@ ${contentText}</agent-response>`;
|
|
|
16597
18115
|
this.eventManager.sendTaskMessageRemoved(this.project.baseDir, this.taskId, messageIds);
|
|
16598
18116
|
}
|
|
16599
18117
|
}
|
|
16600
|
-
async redoUserPrompt(messageId, mode, updatedPrompt) {
|
|
18118
|
+
async redoUserPrompt(messageId, mode, updatedPrompt, updatedImages) {
|
|
16601
18119
|
logger.info("Redoing user prompt:", {
|
|
16602
18120
|
baseDir: this.project.baseDir,
|
|
16603
18121
|
messageId,
|
|
16604
18122
|
mode,
|
|
16605
|
-
hasUpdatedPrompt: !!updatedPrompt
|
|
18123
|
+
hasUpdatedPrompt: !!updatedPrompt,
|
|
18124
|
+
hasUpdatedImages: !!updatedImages
|
|
16606
18125
|
});
|
|
16607
18126
|
const removedMessages = this.contextManager.removeMessagesUpToUserMessage(messageId);
|
|
16608
18127
|
const originalUserMessage = removedMessages[0];
|
|
@@ -16610,14 +18129,16 @@ ${contentText}</agent-response>`;
|
|
|
16610
18129
|
logger.warn("Could not find the specified user message to redo.", { messageId });
|
|
16611
18130
|
return;
|
|
16612
18131
|
}
|
|
16613
|
-
const
|
|
18132
|
+
const originalText = extractTextContent(originalUserMessage.content);
|
|
18133
|
+
const promptToRun = updatedPrompt ?? originalText;
|
|
18134
|
+
const imagesToRun = updatedImages ?? (updatedPrompt !== void 0 ? void 0 : extractImagesFromContent(originalUserMessage.content));
|
|
16614
18135
|
if (promptToRun) {
|
|
16615
18136
|
logger.info("Found message content to run, reloading and re-running prompt.", {
|
|
16616
18137
|
remainingMessagesCount: (await this.contextManager.getContextMessages()).length
|
|
16617
18138
|
});
|
|
16618
18139
|
this.sendTaskMessageRemoved(removedMessages.slice(1).map((msg) => msg.id));
|
|
16619
18140
|
await this.updateContextInfo();
|
|
16620
|
-
void this.runPrompt(promptToRun, mode, false, originalUserMessage.id);
|
|
18141
|
+
void this.runPrompt(promptToRun, mode, false, originalUserMessage.id, true, imagesToRun);
|
|
16621
18142
|
} else {
|
|
16622
18143
|
logger.warn("Could not find a previous user message to redo or an updated prompt to run.");
|
|
16623
18144
|
}
|
|
@@ -16713,6 +18234,22 @@ ${contentText}</agent-response>`;
|
|
|
16713
18234
|
}
|
|
16714
18235
|
return skillMessages;
|
|
16715
18236
|
}
|
|
18237
|
+
async smartCompactConversation(contextMessages, infoMessage = "Conversation smart-compacted.") {
|
|
18238
|
+
if (!contextMessages) {
|
|
18239
|
+
contextMessages = await this.contextManager.getContextMessages();
|
|
18240
|
+
}
|
|
18241
|
+
if (contextMessages.length === 0) {
|
|
18242
|
+
this.addLogMessage("warning", "No conversation to compact.");
|
|
18243
|
+
return [];
|
|
18244
|
+
}
|
|
18245
|
+
await this.contextManager.backupContext();
|
|
18246
|
+
const compactedMessages = await smartCompactMessages(contextMessages);
|
|
18247
|
+
this.contextManager.setContextMessages(compactedMessages);
|
|
18248
|
+
await this.contextManager.loadMessages(compactedMessages, false);
|
|
18249
|
+
await this.updateContextInfo();
|
|
18250
|
+
this.addLogMessage("info", infoMessage, false, void 0, ["undoContextChange"]);
|
|
18251
|
+
return compactedMessages;
|
|
18252
|
+
}
|
|
16716
18253
|
async compactConversation(mode, customInstructions, profile = null, contextMessages, promptContext, abortSignal, waitForAgentCompletion = true, loadingMessage = "Compacting conversation...") {
|
|
16717
18254
|
if (!profile) {
|
|
16718
18255
|
profile = await this.getTaskAgentProfile();
|
|
@@ -16801,7 +18338,7 @@ ${contentText}</agent-response>`;
|
|
|
16801
18338
|
await this.contextManager.loadMessages(await this.contextManager.getContextMessages());
|
|
16802
18339
|
}
|
|
16803
18340
|
await this.updateContextInfo();
|
|
16804
|
-
this.addLogMessage("info", "Conversation compacted.");
|
|
18341
|
+
this.addLogMessage("info", "Conversation compacted.", false, void 0, ["undoContextChange"]);
|
|
16805
18342
|
} catch (error) {
|
|
16806
18343
|
logger.error("Failed to compact conversation", {
|
|
16807
18344
|
baseDir: this.project.baseDir,
|
|
@@ -16869,7 +18406,7 @@ ${contentText}</agent-response>`;
|
|
|
16869
18406
|
const newTaskData = await this.project.createNewTask({
|
|
16870
18407
|
parentId: this.task.parentId || this.taskId,
|
|
16871
18408
|
sendEvent: false,
|
|
16872
|
-
|
|
18409
|
+
autonomyMode: execute ? this.task.autonomyMode : void 0,
|
|
16873
18410
|
activate: true
|
|
16874
18411
|
});
|
|
16875
18412
|
const newTask = this.project.getTask(newTaskData.id);
|
|
@@ -16899,6 +18436,7 @@ ${contentText}</agent-response>`;
|
|
|
16899
18436
|
}
|
|
16900
18437
|
async updateContextInfo(checkContextFilesIncluded = false, checkRepoMapIncluded = false) {
|
|
16901
18438
|
void this.debouncedUpdateContextInfo(checkContextFilesIncluded, checkRepoMapIncluded);
|
|
18439
|
+
void this.sendSkillsUpdated();
|
|
16902
18440
|
}
|
|
16903
18441
|
debouncedUpdateContextInfo = debounce(async (checkContextFilesIncluded = false, checkRepoMapIncluded = false) => {
|
|
16904
18442
|
void this.sendRequestContextInfo();
|
|
@@ -17172,7 +18710,6 @@ ${error.stderr}`,
|
|
|
17172
18710
|
id: uuid.v4()
|
|
17173
18711
|
};
|
|
17174
18712
|
this.addUserMessage(promptContext.id, prompt);
|
|
17175
|
-
this.addLogMessage("loading", "Executing custom command...");
|
|
17176
18713
|
try {
|
|
17177
18714
|
if (!AIDER_MODES.includes(mode)) {
|
|
17178
18715
|
const profile = await this.getTaskAgentProfile();
|
|
@@ -17180,11 +18717,27 @@ ${error.stderr}`,
|
|
|
17180
18717
|
this.addLogMessage("error", "No active Agent profile found");
|
|
17181
18718
|
return;
|
|
17182
18719
|
}
|
|
17183
|
-
|
|
18720
|
+
if (command.skills?.length) {
|
|
18721
|
+
for (const skillName of command.skills) {
|
|
18722
|
+
try {
|
|
18723
|
+
await this.activateSkill(skillName);
|
|
18724
|
+
} catch (error) {
|
|
18725
|
+
logger.warn(`Failed to activate skill '${skillName}' for command '${commandName}': ${error instanceof Error ? error.message : String(error)}`);
|
|
18726
|
+
}
|
|
18727
|
+
}
|
|
18728
|
+
}
|
|
18729
|
+
const systemPrompt = await this.promptsManager.getSystemPrompt(
|
|
18730
|
+
this.store.getSettings(),
|
|
18731
|
+
this,
|
|
18732
|
+
profile,
|
|
18733
|
+
command.autonomyMode ?? this.task.autonomyMode ?? DEFAULT_AUTONOMY_MODE
|
|
18734
|
+
);
|
|
17184
18735
|
const messages = command.includeContext === false ? [] : void 0;
|
|
17185
18736
|
const contextFiles = command.includeContext === false ? [] : void 0;
|
|
18737
|
+
this.addLogMessage("loading", "Executing custom command...");
|
|
17186
18738
|
await this.runPromptInAgent(profile, mode, prompt, promptContext, messages, contextFiles, systemPrompt);
|
|
17187
18739
|
} else {
|
|
18740
|
+
this.addLogMessage("loading", "Executing custom command...");
|
|
17188
18741
|
await this.runPromptInAider(mode, prompt, promptContext);
|
|
17189
18742
|
}
|
|
17190
18743
|
} finally {
|
|
@@ -17929,7 +19482,7 @@ ${diff}
|
|
|
17929
19482
|
const newTaskData = await this.project.createNewTask({
|
|
17930
19483
|
name: taskName,
|
|
17931
19484
|
sendEvent: false,
|
|
17932
|
-
|
|
19485
|
+
autonomyMode: AutonomyMode.Autonomous,
|
|
17933
19486
|
activate: true,
|
|
17934
19487
|
mode,
|
|
17935
19488
|
parentId: this.task.parentId || this.taskId,
|
|
@@ -18069,7 +19622,7 @@ class Project {
|
|
|
18069
19622
|
this.promptsManager = promptsManager;
|
|
18070
19623
|
this.extensionManager = extensionManager;
|
|
18071
19624
|
this.pythonInstaller = pythonInstaller;
|
|
18072
|
-
this.customCommandManager = new CustomCommandManager(this, this.eventManager, this.extensionManager);
|
|
19625
|
+
this.customCommandManager = new CustomCommandManager(this, this.eventManager, this.extensionManager, this.store);
|
|
18073
19626
|
this.tasksLoadingPromise = this.loadTasks();
|
|
18074
19627
|
}
|
|
18075
19628
|
customCommandManager;
|
|
@@ -18106,6 +19659,9 @@ class Project {
|
|
|
18106
19659
|
if (!parentTask) {
|
|
18107
19660
|
throw new Error(`Parent task with id ${normalizedParams.parentId} not found`);
|
|
18108
19661
|
}
|
|
19662
|
+
if (parentTask.task.parentId) {
|
|
19663
|
+
normalizedParams.parentId = parentTask.task.parentId;
|
|
19664
|
+
}
|
|
18109
19665
|
if (!parentTask.task.createdAt) {
|
|
18110
19666
|
await parentTask.saveTask();
|
|
18111
19667
|
}
|
|
@@ -18142,11 +19698,7 @@ class Project {
|
|
|
18142
19698
|
...normalizedParams
|
|
18143
19699
|
};
|
|
18144
19700
|
}
|
|
18145
|
-
const projectSettings = this.getProjectSettings();
|
|
18146
|
-
const taskSettings = this.store.getSettings().taskSettings;
|
|
18147
19701
|
const taskData = {
|
|
18148
|
-
contextCompactingThreshold: taskSettings.contextCompactingThreshold,
|
|
18149
|
-
autoApprove: projectSettings.autoApproveLocked ? true : initialTaskData.autoApprove,
|
|
18150
19702
|
...initialTaskData
|
|
18151
19703
|
};
|
|
18152
19704
|
const extResult = await this.extensionManager.dispatchEvent("onTaskCreated", { task: taskData }, this);
|
|
@@ -18164,7 +19716,14 @@ class Project {
|
|
|
18164
19716
|
}
|
|
18165
19717
|
return task.task;
|
|
18166
19718
|
}
|
|
18167
|
-
|
|
19719
|
+
generateShortTaskId() {
|
|
19720
|
+
let id;
|
|
19721
|
+
do {
|
|
19722
|
+
id = uuid.v4().substring(0, 8);
|
|
19723
|
+
} while (this.tasks.has(id));
|
|
19724
|
+
return id;
|
|
19725
|
+
}
|
|
19726
|
+
async prepareTask(taskId = this.generateShortTaskId(), initialTaskData) {
|
|
18168
19727
|
const task = new Task(
|
|
18169
19728
|
this,
|
|
18170
19729
|
taskId,
|
|
@@ -18333,6 +19892,9 @@ class Project {
|
|
|
18333
19892
|
getAgentProfiles() {
|
|
18334
19893
|
return this.agentProfileManager.getProjectProfiles(this);
|
|
18335
19894
|
}
|
|
19895
|
+
resolveAgentProfile(id) {
|
|
19896
|
+
return this.agentProfileManager.resolveAgentProfile(id);
|
|
19897
|
+
}
|
|
18336
19898
|
/**
|
|
18337
19899
|
* Checks if any other task (excluding the specified taskId) uses the given worktree path.
|
|
18338
19900
|
*/
|
|
@@ -18438,6 +20000,7 @@ class Project {
|
|
|
18438
20000
|
this.forEachTask((task) => {
|
|
18439
20001
|
void task.settingsChanged(oldSettings, newSettings);
|
|
18440
20002
|
});
|
|
20003
|
+
void this.customCommandManager.settingsChanged(oldSettings, newSettings);
|
|
18441
20004
|
}
|
|
18442
20005
|
async projectSettingsChanged(oldSettings, newSettings) {
|
|
18443
20006
|
this.forEachTask((task) => {
|
|
@@ -18588,6 +20151,10 @@ class EventManager {
|
|
|
18588
20151
|
this.sendToWindows("clear-task", data);
|
|
18589
20152
|
this.broadcastToEventConnectors("clear-task", data);
|
|
18590
20153
|
}
|
|
20154
|
+
sendContextInfoUpdated(data) {
|
|
20155
|
+
this.sendToWindows("context-info-updated", data);
|
|
20156
|
+
this.broadcastToEventConnectors("context-info-updated", data);
|
|
20157
|
+
}
|
|
18591
20158
|
// File management events
|
|
18592
20159
|
sendFileAdded(baseDir, taskId, file) {
|
|
18593
20160
|
const data = {
|
|
@@ -18683,7 +20250,8 @@ class EventManager {
|
|
|
18683
20250
|
baseDir,
|
|
18684
20251
|
taskId,
|
|
18685
20252
|
command,
|
|
18686
|
-
output
|
|
20253
|
+
output,
|
|
20254
|
+
timestamp: Date.now()
|
|
18687
20255
|
};
|
|
18688
20256
|
this.sendToWindows("command-output", data);
|
|
18689
20257
|
this.broadcastToEventConnectors("command-output", data);
|
|
@@ -20979,7 +22547,8 @@ const createLmStudioLlm = (profile, model, settings, projectDir) => {
|
|
|
20979
22547
|
const lmStudioProvider = openaiCompatible.createOpenAICompatible({
|
|
20980
22548
|
name: "lmstudio",
|
|
20981
22549
|
baseURL: baseUrl,
|
|
20982
|
-
headers: profile.headers
|
|
22550
|
+
headers: profile.headers,
|
|
22551
|
+
includeUsage: true
|
|
20983
22552
|
});
|
|
20984
22553
|
return lmStudioProvider(model.id);
|
|
20985
22554
|
};
|
|
@@ -21291,6 +22860,117 @@ const mistralProviderStrategy = {
|
|
|
21291
22860
|
getAiderMapping: getMistralAiderMapping,
|
|
21292
22861
|
getModelInfo: getDefaultModelInfo
|
|
21293
22862
|
};
|
|
22863
|
+
const NEURALWATT_BASE_URL = "https://api.neuralwatt.com/v1";
|
|
22864
|
+
const loadNeuralwattModels = async (profile, settings) => {
|
|
22865
|
+
if (!isNeuralwattProvider(profile.provider)) {
|
|
22866
|
+
return { models: [], success: false };
|
|
22867
|
+
}
|
|
22868
|
+
const provider = profile.provider;
|
|
22869
|
+
const apiKey = provider.apiKey || "";
|
|
22870
|
+
const apiKeyEnv = getEffectiveEnvironmentVariable("NEURALWATT_API_KEY", settings);
|
|
22871
|
+
const effectiveApiKey = apiKey || apiKeyEnv?.value || "";
|
|
22872
|
+
if (!effectiveApiKey) {
|
|
22873
|
+
logger.debug("Neuralwatt API key is required. Please set it in Providers settings or via NEURALWATT_API_KEY environment variable.");
|
|
22874
|
+
return { models: [], success: false };
|
|
22875
|
+
}
|
|
22876
|
+
try {
|
|
22877
|
+
const response = await fetch(`${NEURALWATT_BASE_URL}/models`, {
|
|
22878
|
+
headers: { Authorization: `Bearer ${effectiveApiKey}` }
|
|
22879
|
+
});
|
|
22880
|
+
if (!response.ok) {
|
|
22881
|
+
const errorMsg = `Neuralwatt models API response failed: ${response.status} ${response.statusText} ${await response.text()}`;
|
|
22882
|
+
logger.error(errorMsg, { status: response.status, statusText: response.statusText });
|
|
22883
|
+
return { models: [], success: false, error: errorMsg };
|
|
22884
|
+
}
|
|
22885
|
+
const data = await response.json();
|
|
22886
|
+
logger.info(`Received response from Neuralwatt models API for profile ${profile.id}`, { data });
|
|
22887
|
+
const models = data.data?.map((model) => {
|
|
22888
|
+
const metadata = model.metadata;
|
|
22889
|
+
const pricing = metadata?.pricing;
|
|
22890
|
+
const limits = metadata?.limits;
|
|
22891
|
+
const capabilities = metadata?.capabilities;
|
|
22892
|
+
return {
|
|
22893
|
+
id: model.id,
|
|
22894
|
+
providerId: profile.id,
|
|
22895
|
+
maxInputTokens: limits?.max_context_length ?? model.max_model_len,
|
|
22896
|
+
maxOutputTokensLimit: limits?.max_output_tokens ?? void 0,
|
|
22897
|
+
inputCostPerToken: pricing ? pricing.input_per_million / 1e6 : void 0,
|
|
22898
|
+
outputCostPerToken: pricing ? pricing.output_per_million / 1e6 : void 0,
|
|
22899
|
+
cacheReadInputTokenCost: pricing?.cached_input_per_million != null ? pricing.cached_input_per_million / 1e6 : void 0,
|
|
22900
|
+
supportsTools: capabilities?.tools
|
|
22901
|
+
};
|
|
22902
|
+
}) || [];
|
|
22903
|
+
logger.info(`Loaded ${models.length} Neuralwatt models for profile ${profile.id}`);
|
|
22904
|
+
return { models, success: true };
|
|
22905
|
+
} catch (error) {
|
|
22906
|
+
const errorMsg = typeof error === "string" ? error : error instanceof Error ? error.message : "Unknown error loading Neuralwatt models";
|
|
22907
|
+
logger.error("Error loading Neuralwatt models:", error);
|
|
22908
|
+
return { models: [], success: false, error: errorMsg };
|
|
22909
|
+
}
|
|
22910
|
+
};
|
|
22911
|
+
const hasNeuralwattEnvVars = (settings) => {
|
|
22912
|
+
return !!getEffectiveEnvironmentVariable("NEURALWATT_API_KEY", settings, void 0)?.value;
|
|
22913
|
+
};
|
|
22914
|
+
const getNeuralwattAiderMapping = (provider, modelId) => {
|
|
22915
|
+
const neuralwattProvider = provider.provider;
|
|
22916
|
+
const envVars = {};
|
|
22917
|
+
if (neuralwattProvider.apiKey) {
|
|
22918
|
+
envVars.OPENAI_API_KEY = neuralwattProvider.apiKey;
|
|
22919
|
+
}
|
|
22920
|
+
envVars.OPENAI_API_BASE = NEURALWATT_BASE_URL;
|
|
22921
|
+
return {
|
|
22922
|
+
modelName: `openai/${modelId}`,
|
|
22923
|
+
environmentVariables: envVars
|
|
22924
|
+
};
|
|
22925
|
+
};
|
|
22926
|
+
const createNeuralwattLlm = (profile, model, settings, projectDir) => {
|
|
22927
|
+
const provider = profile.provider;
|
|
22928
|
+
let apiKey = provider.apiKey;
|
|
22929
|
+
if (!apiKey) {
|
|
22930
|
+
const effectiveVar = getEffectiveEnvironmentVariable("NEURALWATT_API_KEY", settings, projectDir);
|
|
22931
|
+
if (effectiveVar) {
|
|
22932
|
+
apiKey = effectiveVar.value;
|
|
22933
|
+
logger.debug(`Loaded NEURALWATT_API_KEY from ${effectiveVar.source}`);
|
|
22934
|
+
}
|
|
22935
|
+
}
|
|
22936
|
+
if (!apiKey) {
|
|
22937
|
+
throw new Error("Neuralwatt API key is required in Providers settings or Aider environment variables (NEURALWATT_API_KEY)");
|
|
22938
|
+
}
|
|
22939
|
+
const compatibleProvider = openaiCompatible.createOpenAICompatible({
|
|
22940
|
+
name: "neuralwatt",
|
|
22941
|
+
apiKey,
|
|
22942
|
+
baseURL: NEURALWATT_BASE_URL,
|
|
22943
|
+
headers: profile.headers
|
|
22944
|
+
});
|
|
22945
|
+
return compatibleProvider(model.id);
|
|
22946
|
+
};
|
|
22947
|
+
const getNeuralwattUsageReport = (task, provider, model, usage, providerMetadata) => {
|
|
22948
|
+
const totalSentTokens = usage.inputTokens || 0;
|
|
22949
|
+
const receivedTokens = usage.outputTokens || 0;
|
|
22950
|
+
const cacheReadTokens = usage.cachedInputTokens || 0;
|
|
22951
|
+
const sentTokens = totalSentTokens - cacheReadTokens;
|
|
22952
|
+
logger.info("Neuralwatt usage report", {
|
|
22953
|
+
providerMetadata,
|
|
22954
|
+
usage
|
|
22955
|
+
});
|
|
22956
|
+
const messageCost = calculateCost(model, sentTokens, receivedTokens, cacheReadTokens);
|
|
22957
|
+
return {
|
|
22958
|
+
model: `${provider.id}/${model.id}`,
|
|
22959
|
+
sentTokens,
|
|
22960
|
+
receivedTokens,
|
|
22961
|
+
cacheReadTokens,
|
|
22962
|
+
messageCost,
|
|
22963
|
+
agentTotalCost: task.task.agentTotalCost + messageCost
|
|
22964
|
+
};
|
|
22965
|
+
};
|
|
22966
|
+
const neuralwattProviderStrategy = {
|
|
22967
|
+
createLlm: createNeuralwattLlm,
|
|
22968
|
+
getUsageReport: getNeuralwattUsageReport,
|
|
22969
|
+
loadModels: loadNeuralwattModels,
|
|
22970
|
+
hasEnvVars: hasNeuralwattEnvVars,
|
|
22971
|
+
getAiderMapping: getNeuralwattAiderMapping,
|
|
22972
|
+
getModelInfo: getDefaultModelInfo
|
|
22973
|
+
};
|
|
21294
22974
|
const loadOllamaModels = async (profile, settings) => {
|
|
21295
22975
|
if (!isOllamaProvider(profile.provider)) {
|
|
21296
22976
|
return { models: [], success: false };
|
|
@@ -21652,12 +23332,18 @@ const loadOpenaiCompatibleModels = async (profile, settings) => {
|
|
|
21652
23332
|
return { models: [], success: false, error: errorMsg };
|
|
21653
23333
|
}
|
|
21654
23334
|
const data = await response.json();
|
|
21655
|
-
const models = data.data?.map(
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
21660
|
-
|
|
23335
|
+
const models = data.data?.map(
|
|
23336
|
+
(model) => {
|
|
23337
|
+
const maxInputTokens = model.max_model_len ?? model.context_length ?? model.num_ctx ?? model.context_window;
|
|
23338
|
+
const maxOutputTokensLimit = model.max_completion_tokens ?? model.max_tokens;
|
|
23339
|
+
return {
|
|
23340
|
+
id: model.id,
|
|
23341
|
+
providerId: profile.id,
|
|
23342
|
+
...maxInputTokens != null && { maxInputTokens },
|
|
23343
|
+
...maxOutputTokensLimit != null && { maxOutputTokensLimit }
|
|
23344
|
+
};
|
|
23345
|
+
}
|
|
23346
|
+
) || [];
|
|
21661
23347
|
logger.info(`Loaded ${models.length} OpenAI-compatible models for profile ${profile.id}`);
|
|
21662
23348
|
return { models, success: true };
|
|
21663
23349
|
} catch (error) {
|
|
@@ -21706,11 +23392,14 @@ const createOpenAiCompatibleLlm = (profile, model, settings, projectDir) => {
|
|
|
21706
23392
|
if (!baseUrl) {
|
|
21707
23393
|
throw new Error(`Base URL is required for ${provider.name} provider. Set it in Providers settings or via the OPENAI_API_BASE environment variable.`);
|
|
21708
23394
|
}
|
|
23395
|
+
const providerOverrides = model.providerOverrides;
|
|
23396
|
+
const trackTokenUsage = providerOverrides?.trackTokenUsage ?? provider.trackTokenUsage;
|
|
21709
23397
|
const compatibleProvider = openaiCompatible.createOpenAICompatible({
|
|
21710
23398
|
name: provider.name,
|
|
21711
23399
|
apiKey,
|
|
21712
23400
|
baseURL: baseUrl,
|
|
21713
|
-
headers: profile.headers
|
|
23401
|
+
headers: profile.headers,
|
|
23402
|
+
includeUsage: trackTokenUsage !== false
|
|
21714
23403
|
});
|
|
21715
23404
|
return compatibleProvider(model.id);
|
|
21716
23405
|
};
|
|
@@ -22192,7 +23881,7 @@ const getRequestyUsageReport = (task, provider, model, usage, providerMetadata)
|
|
|
22192
23881
|
const cacheWriteTokens = requesty?.usage?.cachingTokens ?? 0;
|
|
22193
23882
|
const cacheReadTokens = requesty?.usage?.cachedTokens ?? 0;
|
|
22194
23883
|
const sentTokens = totalSentTokens - cacheReadTokens;
|
|
22195
|
-
const messageCost = calculateRequestyCost(model, sentTokens, receivedTokens, cacheWriteTokens, cacheReadTokens);
|
|
23884
|
+
const messageCost = requesty?.usage?.cost ?? calculateRequestyCost(model, sentTokens, receivedTokens, cacheWriteTokens, cacheReadTokens);
|
|
22196
23885
|
return {
|
|
22197
23886
|
model: `${provider.id}/${model.id}`,
|
|
22198
23887
|
sentTokens,
|
|
@@ -22645,6 +24334,7 @@ class ModelManager {
|
|
|
22645
24334
|
lmstudio: lmStudioProviderStrategy,
|
|
22646
24335
|
minimax: minimaxProviderStrategy,
|
|
22647
24336
|
mistral: mistralProviderStrategy,
|
|
24337
|
+
neuralwatt: neuralwattProviderStrategy,
|
|
22648
24338
|
ollama: ollamaProviderStrategy,
|
|
22649
24339
|
openai: openaiProviderStrategy,
|
|
22650
24340
|
"openai-compatible": openaiCompatibleProviderStrategy,
|
|
@@ -23102,6 +24792,7 @@ class ModelManager {
|
|
|
23102
24792
|
getModelSettings(providerId, modelId, useModelInfoFallback = false) {
|
|
23103
24793
|
let model;
|
|
23104
24794
|
const providerModels = this.providerModels[providerId];
|
|
24795
|
+
logger.debug(`getModelSettings providerModels for provider: ${providerId}`, { providerModels });
|
|
23105
24796
|
if (providerModels) {
|
|
23106
24797
|
model = providerModels.find((m) => m.id === modelId);
|
|
23107
24798
|
}
|
|
@@ -23115,6 +24806,11 @@ class ModelManager {
|
|
|
23115
24806
|
};
|
|
23116
24807
|
}
|
|
23117
24808
|
}
|
|
24809
|
+
logger.debug("getModelSettings model", {
|
|
24810
|
+
providerId,
|
|
24811
|
+
modelId,
|
|
24812
|
+
model
|
|
24813
|
+
});
|
|
23118
24814
|
return model;
|
|
23119
24815
|
}
|
|
23120
24816
|
createLlm(provider, model, settings, projectDir, toolSet, systemPrompt, providerMetadata) {
|
|
@@ -23680,7 +25376,7 @@ class VersionsManager {
|
|
|
23680
25376
|
...this.versionsInfo
|
|
23681
25377
|
};
|
|
23682
25378
|
}
|
|
23683
|
-
logger.
|
|
25379
|
+
logger.debug("Checking for version updates...");
|
|
23684
25380
|
const aiderDeskCurrentVersion = app.getVersion();
|
|
23685
25381
|
const autoUpdater = require("electron-updater").autoUpdater;
|
|
23686
25382
|
if (this.pythonInstaller) {
|
|
@@ -23990,7 +25686,7 @@ class MemoryManager {
|
|
|
23990
25686
|
done: this.embeddingProgress.done,
|
|
23991
25687
|
total: this.embeddingProgress.total,
|
|
23992
25688
|
finished: true,
|
|
23993
|
-
error: "Failed to load @huggingface/transformers. This is usually a packaging issue with native dependencies
|
|
25689
|
+
error: "Failed to load @huggingface/transformers. This is usually a packaging issue with native dependencies."
|
|
23994
25690
|
};
|
|
23995
25691
|
logger.error("Failed to load transformers module. Memory embedding will be unavailable.", error);
|
|
23996
25692
|
return;
|
|
@@ -24162,7 +25858,7 @@ class MemoryManager {
|
|
|
24162
25858
|
done: this.embeddingProgress.done,
|
|
24163
25859
|
total: this.embeddingProgress.total,
|
|
24164
25860
|
finished: true,
|
|
24165
|
-
error: "Failed to load @huggingface/transformers. This is usually a packaging issue with native dependencies
|
|
25861
|
+
error: "Failed to load @huggingface/transformers. This is usually a packaging issue with native dependencies."
|
|
24166
25862
|
};
|
|
24167
25863
|
logger.error("Failed to load transformers module. Memory embedding will be unavailable.", error);
|
|
24168
25864
|
return;
|
|
@@ -24818,6 +26514,18 @@ class ExtensionContextImpl {
|
|
|
24818
26514
|
return [];
|
|
24819
26515
|
}
|
|
24820
26516
|
}
|
|
26517
|
+
getProviders() {
|
|
26518
|
+
if (!this.modelManager) {
|
|
26519
|
+
this.log("ModelManager not available, returning empty providers", "warn");
|
|
26520
|
+
return [];
|
|
26521
|
+
}
|
|
26522
|
+
try {
|
|
26523
|
+
return this.modelManager.getProviders();
|
|
26524
|
+
} catch (error) {
|
|
26525
|
+
this.log(`Failed to get providers: ${error}`, "error");
|
|
26526
|
+
return [];
|
|
26527
|
+
}
|
|
26528
|
+
}
|
|
24821
26529
|
async getSetting(key) {
|
|
24822
26530
|
if (!this.store) {
|
|
24823
26531
|
throw new Error("Store not available");
|
|
@@ -24908,6 +26616,9 @@ class ExtensionContextImpl {
|
|
|
24908
26616
|
}
|
|
24909
26617
|
return this.memoryManager;
|
|
24910
26618
|
}
|
|
26619
|
+
async truncateToolResult(content, maxLines, maxSizeKB, maxTokens, saveToFile, truncationSuffix) {
|
|
26620
|
+
return truncateToolResult(content, maxLines, maxSizeKB, maxTokens, saveToFile, truncationSuffix);
|
|
26621
|
+
}
|
|
24911
26622
|
}
|
|
24912
26623
|
const CACHE_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
24913
26624
|
class ExtensionFetcher {
|
|
@@ -25036,6 +26747,41 @@ class ExtensionFetcher {
|
|
|
25036
26747
|
const extensions = [];
|
|
25037
26748
|
try {
|
|
25038
26749
|
const entries = await fs.readdir(extensionsPath, { withFileTypes: true });
|
|
26750
|
+
const rootIndexTs = path.join(extensionsPath, "index.ts");
|
|
26751
|
+
const rootIndexJs = path.join(extensionsPath, "index.js");
|
|
26752
|
+
const rootPackageJson = path.join(extensionsPath, "package.json");
|
|
26753
|
+
let rootIndexFile = null;
|
|
26754
|
+
if (await this.fileExists(rootIndexTs)) {
|
|
26755
|
+
rootIndexFile = rootIndexTs;
|
|
26756
|
+
} else if (await this.fileExists(rootIndexJs)) {
|
|
26757
|
+
rootIndexFile = rootIndexJs;
|
|
26758
|
+
}
|
|
26759
|
+
const hasRootPackageJson = await this.fileExists(rootPackageJson);
|
|
26760
|
+
if (rootIndexFile && hasRootPackageJson) {
|
|
26761
|
+
const metadata = await this.extractMetadataFromLocalFile(rootIndexFile);
|
|
26762
|
+
if (metadata) {
|
|
26763
|
+
const repoName = this.getRepoName(repoUrl);
|
|
26764
|
+
let readmeContent;
|
|
26765
|
+
const readmePath = path.join(extensionsPath, "README.md");
|
|
26766
|
+
try {
|
|
26767
|
+
const content = await fs.readFile(readmePath, "utf-8");
|
|
26768
|
+
if (content.trim()) {
|
|
26769
|
+
readmeContent = content;
|
|
26770
|
+
}
|
|
26771
|
+
} catch {
|
|
26772
|
+
}
|
|
26773
|
+
extensions.push({
|
|
26774
|
+
...metadata,
|
|
26775
|
+
id: repoName,
|
|
26776
|
+
type: "folder",
|
|
26777
|
+
folder: repoName,
|
|
26778
|
+
repositoryUrl: repoUrl,
|
|
26779
|
+
hasDependencies: true,
|
|
26780
|
+
readmeContent
|
|
26781
|
+
});
|
|
26782
|
+
return extensions;
|
|
26783
|
+
}
|
|
26784
|
+
}
|
|
25039
26785
|
for (const entry of entries) {
|
|
25040
26786
|
if (entry.isDirectory()) {
|
|
25041
26787
|
const indexPathTs = path.join(extensionsPath, entry.name, "index.ts");
|
|
@@ -25089,6 +26835,17 @@ class ExtensionFetcher {
|
|
|
25089
26835
|
}
|
|
25090
26836
|
return extensions;
|
|
25091
26837
|
}
|
|
26838
|
+
getRepoName(repoUrl) {
|
|
26839
|
+
try {
|
|
26840
|
+
const url = new URL(repoUrl);
|
|
26841
|
+
const pathParts = url.pathname.split("/").filter(Boolean);
|
|
26842
|
+
if (pathParts.length >= 2) {
|
|
26843
|
+
return pathParts[1].replace(/\.git$/, "");
|
|
26844
|
+
}
|
|
26845
|
+
} catch {
|
|
26846
|
+
}
|
|
26847
|
+
return repoUrl.replace(/[^a-zA-Z0-9]/g, "-");
|
|
26848
|
+
}
|
|
25092
26849
|
async fileExists(filePath) {
|
|
25093
26850
|
try {
|
|
25094
26851
|
await fs.access(filePath);
|
|
@@ -25267,6 +27024,9 @@ class ExtensionManager {
|
|
|
25267
27024
|
logger.debug("[Extensions] Repository list changed, refreshing extension cache");
|
|
25268
27025
|
void this.fetcher.getAvailableExtensions(newRepositories, true);
|
|
25269
27026
|
}
|
|
27027
|
+
if (oldSettings.fileWatchMode !== newSettings.fileWatchMode) {
|
|
27028
|
+
void this.restartWatchers();
|
|
27029
|
+
}
|
|
25270
27030
|
}
|
|
25271
27031
|
addListener(listener) {
|
|
25272
27032
|
this.listeners.push(listener);
|
|
@@ -25571,7 +27331,7 @@ class ExtensionManager {
|
|
|
25571
27331
|
};
|
|
25572
27332
|
const watcher = chokidar.watch(dir, {
|
|
25573
27333
|
persistent: true,
|
|
25574
|
-
usePolling:
|
|
27334
|
+
usePolling: shouldUsePolling(dir, this.store.getSettings().fileWatchMode),
|
|
25575
27335
|
ignoreInitial: true,
|
|
25576
27336
|
ignored: (filePath) => {
|
|
25577
27337
|
const basename = path.basename(filePath);
|
|
@@ -25655,6 +27415,25 @@ class ExtensionManager {
|
|
|
25655
27415
|
logger.debug(`[Extensions] Stopped watching project extensions: ${projectDir}`);
|
|
25656
27416
|
}
|
|
25657
27417
|
}
|
|
27418
|
+
async restartWatchers() {
|
|
27419
|
+
const projectDirs = Array.from(this.projectWatchers.keys());
|
|
27420
|
+
await this.stopGlobalWatcher();
|
|
27421
|
+
for (const dir of projectDirs) {
|
|
27422
|
+
await this.stopProjectWatcher(dir);
|
|
27423
|
+
}
|
|
27424
|
+
await this.startHotReloadWatcher();
|
|
27425
|
+
for (const dir of projectDirs) {
|
|
27426
|
+
const projectExtensionsDir = path.join(dir, AIDER_DESK_EXTENSIONS_DIR);
|
|
27427
|
+
const watcher = await this.setupWatcherForDir(projectExtensionsDir, async () => {
|
|
27428
|
+
logger.debug(`[Extensions] Project extensions changed for ${dir}, reloading...`);
|
|
27429
|
+
await this.unloadExtensionsForDir(projectExtensionsDir);
|
|
27430
|
+
await this.loadExtensionsForDir(projectExtensionsDir);
|
|
27431
|
+
});
|
|
27432
|
+
if (watcher) {
|
|
27433
|
+
this.projectWatchers.set(dir, watcher);
|
|
27434
|
+
}
|
|
27435
|
+
}
|
|
27436
|
+
}
|
|
25658
27437
|
static TOOL_NAME_REGEX = /^[a-z][a-z0-9_-]*$/;
|
|
25659
27438
|
validateToolDefinition(tool) {
|
|
25660
27439
|
const errors = [];
|
|
@@ -26009,7 +27788,7 @@ class ExtensionManager {
|
|
|
26009
27788
|
}
|
|
26010
27789
|
collectedSkills.push({
|
|
26011
27790
|
...skill,
|
|
26012
|
-
location: "extension"
|
|
27791
|
+
location: skill.location || "extension"
|
|
26013
27792
|
});
|
|
26014
27793
|
}
|
|
26015
27794
|
} catch (error) {
|
|
@@ -26350,12 +28129,19 @@ class ExtensionManager {
|
|
|
26350
28129
|
} else if (extension.type === "folder" && extension.folder) {
|
|
26351
28130
|
const repoDir = await this.fetcher.ensureRepoCloned(repositoryUrl);
|
|
26352
28131
|
const extensionsPath = this.fetcher.getExtensionsPath(repositoryUrl, repoDir);
|
|
26353
|
-
const sourcePath = path.join(extensionsPath, extension.folder);
|
|
26354
28132
|
const targetPath = path.join(targetDir, extension.folder);
|
|
28133
|
+
let sourcePath = path.join(extensionsPath, extension.folder);
|
|
28134
|
+
if (!await this.fileExists(sourcePath)) {
|
|
28135
|
+
sourcePath = extensionsPath;
|
|
28136
|
+
}
|
|
26355
28137
|
if (!await this.fileExists(sourcePath)) {
|
|
26356
28138
|
throw new Error(`Extension folder not found in repository: ${extension.folder}`);
|
|
26357
28139
|
}
|
|
26358
28140
|
await fs.cp(sourcePath, targetPath, { recursive: true });
|
|
28141
|
+
const gitDir = path.join(targetPath, ".git");
|
|
28142
|
+
if (await this.fileExists(gitDir)) {
|
|
28143
|
+
await fs.rm(gitDir, { recursive: true, force: true });
|
|
28144
|
+
}
|
|
26359
28145
|
if (extension.hasDependencies) {
|
|
26360
28146
|
logger.debug(`[Extensions] Installing dependencies for ${extension.name}...`);
|
|
26361
28147
|
await this.installDependencies(targetPath);
|
|
@@ -26480,12 +28266,19 @@ class ExtensionManager {
|
|
|
26480
28266
|
} else if (extension.type === "folder" && extension.folder) {
|
|
26481
28267
|
const repoDir = await this.fetcher.ensureRepoCloned(repositoryUrl);
|
|
26482
28268
|
const extensionsPath = this.fetcher.getExtensionsPath(repositoryUrl, repoDir);
|
|
26483
|
-
const sourcePath = path.join(extensionsPath, extension.folder);
|
|
26484
28269
|
const targetPath = path.join(targetDir, extension.folder);
|
|
28270
|
+
let sourcePath = path.join(extensionsPath, extension.folder);
|
|
28271
|
+
if (!await this.fileExists(sourcePath)) {
|
|
28272
|
+
sourcePath = extensionsPath;
|
|
28273
|
+
}
|
|
26485
28274
|
if (!await this.fileExists(sourcePath)) {
|
|
26486
28275
|
throw new Error(`Extension folder not found in repository: ${extension.folder}`);
|
|
26487
28276
|
}
|
|
26488
28277
|
await fs.cp(sourcePath, targetPath, { recursive: true });
|
|
28278
|
+
const gitDir = path.join(targetPath, ".git");
|
|
28279
|
+
if (await this.fileExists(gitDir)) {
|
|
28280
|
+
await fs.rm(gitDir, { recursive: true, force: true });
|
|
28281
|
+
}
|
|
26489
28282
|
const packageJsonPath = path.join(targetPath, "package.json");
|
|
26490
28283
|
if (await this.fileExists(packageJsonPath)) {
|
|
26491
28284
|
logger.debug(`[Extensions] Installing dependencies for ${extension.name}...`);
|
|
@@ -26535,7 +28328,7 @@ class ExtensionManager {
|
|
|
26535
28328
|
if (result && typeof result === "object") {
|
|
26536
28329
|
const partialEvent = result;
|
|
26537
28330
|
currentEvent = { ...currentEvent, ...partialEvent };
|
|
26538
|
-
if ("blocked" in currentEvent && currentEvent.blocked
|
|
28331
|
+
if ("blocked" in currentEvent && currentEvent.blocked) {
|
|
26539
28332
|
logger.debug(`[Extensions] Event '${String(eventName)}' blocked by extension '${metadata.name}'`);
|
|
26540
28333
|
break;
|
|
26541
28334
|
}
|
|
@@ -26559,7 +28352,7 @@ class ExtensionManager {
|
|
|
26559
28352
|
}
|
|
26560
28353
|
}
|
|
26561
28354
|
class EventsHandler {
|
|
26562
|
-
constructor(projectManager, store, mcpManager, versionsManager, modelManager, telemetryManager, dataManager, terminalManager, cloudflareTunnelManager, eventManager, agentProfileManager, memoryManager, extensionManager, proxyManager, windowManager) {
|
|
28355
|
+
constructor(projectManager, store, mcpManager, versionsManager, modelManager, telemetryManager, dataManager, terminalManager, cloudflareTunnelManager, eventManager, agentProfileManager, memoryManager, extensionManager, proxyManager, promptsManager, windowManager) {
|
|
26563
28356
|
this.projectManager = projectManager;
|
|
26564
28357
|
this.store = store;
|
|
26565
28358
|
this.mcpManager = mcpManager;
|
|
@@ -26574,6 +28367,7 @@ class EventsHandler {
|
|
|
26574
28367
|
this.memoryManager = memoryManager;
|
|
26575
28368
|
this.extensionManager = extensionManager;
|
|
26576
28369
|
this.proxyManager = proxyManager;
|
|
28370
|
+
this.promptsManager = promptsManager;
|
|
26577
28371
|
this.windowManager = windowManager;
|
|
26578
28372
|
}
|
|
26579
28373
|
loadSettings() {
|
|
@@ -26587,6 +28381,8 @@ class EventsHandler {
|
|
|
26587
28381
|
this.telemetryManager.settingsChanged(oldSettings, newSettings);
|
|
26588
28382
|
void this.memoryManager.settingsChanged(oldSettings, newSettings);
|
|
26589
28383
|
this.extensionManager.settingsChanged(oldSettings, newSettings);
|
|
28384
|
+
void this.agentProfileManager.settingsChanged(oldSettings, newSettings);
|
|
28385
|
+
void this.promptsManager.settingsChanged(oldSettings, newSettings);
|
|
26590
28386
|
this.eventManager.sendSettingsUpdated(newSettings);
|
|
26591
28387
|
return this.store.getSettings();
|
|
26592
28388
|
}
|
|
@@ -26704,8 +28500,8 @@ class EventsHandler {
|
|
|
26704
28500
|
const removedIds = await this.projectManager.getProject(baseDir).getTask(taskId)?.removeMessagesUpTo(messageId) ?? [];
|
|
26705
28501
|
this.eventManager.sendTaskMessageRemoved(baseDir, taskId, removedIds);
|
|
26706
28502
|
}
|
|
26707
|
-
async redoUserPrompt(baseDir, taskId, messageId, mode, updatedPrompt) {
|
|
26708
|
-
void this.projectManager.getProject(baseDir).getTask(taskId)?.redoUserPrompt(messageId, mode, updatedPrompt);
|
|
28503
|
+
async redoUserPrompt(baseDir, taskId, messageId, mode, updatedPrompt, updatedImages) {
|
|
28504
|
+
void this.projectManager.getProject(baseDir).getTask(taskId)?.redoUserPrompt(messageId, mode, updatedPrompt, updatedImages);
|
|
26709
28505
|
}
|
|
26710
28506
|
async resumeTask(baseDir, taskId) {
|
|
26711
28507
|
void this.projectManager.getProject(baseDir).getTask(taskId)?.resumeTask();
|
|
@@ -26716,6 +28512,19 @@ class EventsHandler {
|
|
|
26716
28512
|
await task.compactConversation(mode, customInstructions);
|
|
26717
28513
|
}
|
|
26718
28514
|
}
|
|
28515
|
+
async smartCompactConversation(baseDir, taskId) {
|
|
28516
|
+
const task = this.projectManager.getProject(baseDir).getTask(taskId);
|
|
28517
|
+
if (task) {
|
|
28518
|
+
await task.smartCompactConversation();
|
|
28519
|
+
}
|
|
28520
|
+
}
|
|
28521
|
+
async undoContextChange(baseDir, taskId) {
|
|
28522
|
+
const task = this.projectManager.getProject(baseDir).getTask(taskId);
|
|
28523
|
+
if (task) {
|
|
28524
|
+
return task.undoContextChange();
|
|
28525
|
+
}
|
|
28526
|
+
return false;
|
|
28527
|
+
}
|
|
26719
28528
|
async handoffConversation(baseDir, taskId, focus) {
|
|
26720
28529
|
const task = this.projectManager.getProject(baseDir).getTask(taskId);
|
|
26721
28530
|
if (!task) {
|
|
@@ -26800,8 +28609,8 @@ class EventsHandler {
|
|
|
26800
28609
|
applyEdits(baseDir, taskId, edits) {
|
|
26801
28610
|
this.projectManager.getProject(baseDir).getTask(taskId)?.applyEdits(edits);
|
|
26802
28611
|
}
|
|
26803
|
-
async runPrompt(baseDir, taskId, prompt, mode) {
|
|
26804
|
-
return this.projectManager.getProject(baseDir).getTask(taskId)?.runPrompt(prompt, mode) || [];
|
|
28612
|
+
async runPrompt(baseDir, taskId, prompt, mode, images) {
|
|
28613
|
+
return this.projectManager.getProject(baseDir).getTask(taskId)?.runPrompt(prompt, mode, true, void 0, true, images) || [];
|
|
26805
28614
|
}
|
|
26806
28615
|
async savePrompt(baseDir, taskId, prompt) {
|
|
26807
28616
|
return this.projectManager.getProject(baseDir).getTask(taskId)?.savePromptOnly(prompt);
|
|
@@ -27554,8 +29363,9 @@ const registerAllHelpers = () => {
|
|
|
27554
29363
|
registerFormattingHelpers();
|
|
27555
29364
|
};
|
|
27556
29365
|
class PromptsManager {
|
|
27557
|
-
constructor(extensionManager, defaultTemplatesDir = AIDER_DESK_DEFAULT_PROMPTS_DIR, globalPromptsDir = AIDER_DESK_GLOBAL_PROMPTS_DIR) {
|
|
29366
|
+
constructor(extensionManager, store, defaultTemplatesDir = AIDER_DESK_DEFAULT_PROMPTS_DIR, globalPromptsDir = AIDER_DESK_GLOBAL_PROMPTS_DIR) {
|
|
27558
29367
|
this.extensionManager = extensionManager;
|
|
29368
|
+
this.store = store;
|
|
27559
29369
|
this.defaultTemplatesDir = defaultTemplatesDir;
|
|
27560
29370
|
this.globalPromptsDir = globalPromptsDir;
|
|
27561
29371
|
registerAllHelpers();
|
|
@@ -27649,7 +29459,7 @@ class PromptsManager {
|
|
|
27649
29459
|
}
|
|
27650
29460
|
const watcher = chokidar.watch(this.globalPromptsDir, {
|
|
27651
29461
|
persistent: true,
|
|
27652
|
-
usePolling:
|
|
29462
|
+
usePolling: shouldUsePolling(this.globalPromptsDir, this.store.getSettings().fileWatchMode),
|
|
27653
29463
|
ignoreInitial: true
|
|
27654
29464
|
});
|
|
27655
29465
|
const debouncedReload = debounce(async () => {
|
|
@@ -27674,7 +29484,7 @@ class PromptsManager {
|
|
|
27674
29484
|
await this.compileProjectTemplates(projectDir);
|
|
27675
29485
|
const watcher = chokidar.watch(projectPromptsDir, {
|
|
27676
29486
|
persistent: true,
|
|
27677
|
-
usePolling:
|
|
29487
|
+
usePolling: shouldUsePolling(projectPromptsDir, this.store.getSettings().fileWatchMode),
|
|
27678
29488
|
ignoreInitial: true
|
|
27679
29489
|
});
|
|
27680
29490
|
const debouncedReload = debounce(async () => {
|
|
@@ -27703,6 +29513,20 @@ class PromptsManager {
|
|
|
27703
29513
|
this.projectTemplatesCache.clear();
|
|
27704
29514
|
logger.info("PromptsManager disposed");
|
|
27705
29515
|
}
|
|
29516
|
+
async settingsChanged(oldSettings, newSettings) {
|
|
29517
|
+
if (oldSettings.fileWatchMode === newSettings.fileWatchMode) {
|
|
29518
|
+
return;
|
|
29519
|
+
}
|
|
29520
|
+
const watchedProjects = Array.from(this.watchers.keys()).filter((k) => k !== "global");
|
|
29521
|
+
for (const watcher of this.watchers.values()) {
|
|
29522
|
+
await watcher.close();
|
|
29523
|
+
}
|
|
29524
|
+
this.watchers.clear();
|
|
29525
|
+
await this.setupGlobalWatcher();
|
|
29526
|
+
for (const projectDir of watchedProjects) {
|
|
29527
|
+
await this.watchProject(projectDir);
|
|
29528
|
+
}
|
|
29529
|
+
}
|
|
27706
29530
|
async render(name, data, projectDir, task) {
|
|
27707
29531
|
const projectTemplates = this.projectTemplatesCache.get(projectDir);
|
|
27708
29532
|
const projectTemplate = projectTemplates?.get(name);
|
|
@@ -27728,7 +29552,7 @@ class PromptsManager {
|
|
|
27728
29552
|
}
|
|
27729
29553
|
return prompt;
|
|
27730
29554
|
}
|
|
27731
|
-
calculateToolPermissions = (settings, agentProfile,
|
|
29555
|
+
calculateToolPermissions = (settings, agentProfile, autonomyMode) => {
|
|
27732
29556
|
const { usePowerTools = false, useMemoryTools = false, useSkillsTools = false } = agentProfile;
|
|
27733
29557
|
const memoryEnabled = settings.memory.enabled && useMemoryTools;
|
|
27734
29558
|
const isAllowed = (tool) => agentProfile.toolApprovals[tool] !== ToolApprovalState.Never;
|
|
@@ -27757,11 +29581,12 @@ class PromptsManager {
|
|
|
27757
29581
|
skills: {
|
|
27758
29582
|
allowed: useSkillsTools && isAllowed(`${SKILLS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${SKILLS_TOOL_ACTIVATE_SKILL}`)
|
|
27759
29583
|
},
|
|
27760
|
-
|
|
29584
|
+
autonomyMode
|
|
27761
29585
|
};
|
|
27762
29586
|
};
|
|
27763
|
-
getSystemPrompt = async (settings, task, agentProfile,
|
|
27764
|
-
const
|
|
29587
|
+
getSystemPrompt = async (settings, task, agentProfile, autonomyMode, additionalInstructions) => {
|
|
29588
|
+
const effectiveAutonomyMode = autonomyMode ?? task.task.autonomyMode ?? DEFAULT_AUTONOMY_MODE;
|
|
29589
|
+
const toolPermissions = this.calculateToolPermissions(settings, agentProfile, effectiveAutonomyMode);
|
|
27765
29590
|
toolPermissions.powerTools.anyEnabled = Object.values(toolPermissions.powerTools).some((v) => v);
|
|
27766
29591
|
const rulesFiles = await this.getRulesContent(task, agentProfile);
|
|
27767
29592
|
const customInstructions = [agentProfile.customInstructions, additionalInstructions].filter(Boolean).join("\n\n").trim();
|
|
@@ -28170,12 +29995,12 @@ const initManagers = async (store, windowManager) => {
|
|
|
28170
29995
|
extensionManager.init().catch((error) => {
|
|
28171
29996
|
logger.error("[Extensions] Extension system initialization failed, continuing without extensions:", error);
|
|
28172
29997
|
});
|
|
28173
|
-
const promptsManager = new PromptsManager(extensionManager);
|
|
29998
|
+
const promptsManager = new PromptsManager(extensionManager, store);
|
|
28174
29999
|
promptsManager.init().catch((error) => {
|
|
28175
30000
|
logger.error("[Prompts] Prompts system initialization failed:", error);
|
|
28176
30001
|
});
|
|
28177
30002
|
const worktreeManager = new WorktreeManager();
|
|
28178
|
-
const agentProfileManager = new AgentProfileManager(eventManager, extensionManager);
|
|
30003
|
+
const agentProfileManager = new AgentProfileManager(eventManager, extensionManager, store);
|
|
28179
30004
|
agentProfileManager.init().catch((error) => {
|
|
28180
30005
|
logger.error("[AgentProfile] Agent profile system initialization failed:", error);
|
|
28181
30006
|
});
|
|
@@ -28212,6 +30037,7 @@ const initManagers = async (store, windowManager) => {
|
|
|
28212
30037
|
memoryManager,
|
|
28213
30038
|
extensionManager,
|
|
28214
30039
|
proxyManager,
|
|
30040
|
+
promptsManager,
|
|
28215
30041
|
windowManager
|
|
28216
30042
|
);
|
|
28217
30043
|
const serverController = new ServerController(httpServer, projectManager, eventsHandler, store, pythonInstaller);
|
|
@@ -28760,6 +30586,16 @@ const migrateSettingsV18toV19 = (settings) => {
|
|
|
28760
30586
|
}
|
|
28761
30587
|
};
|
|
28762
30588
|
};
|
|
30589
|
+
const migrateSettingsV19toV20 = (settings) => {
|
|
30590
|
+
const oldThreshold = settings.taskSettings?.contextCompactingThreshold;
|
|
30591
|
+
return {
|
|
30592
|
+
...settings,
|
|
30593
|
+
taskSettings: {
|
|
30594
|
+
...settings.taskSettings,
|
|
30595
|
+
contextCompactingThreshold: typeof oldThreshold === "number" ? { percentage: oldThreshold, tokens: 1e5 } : oldThreshold || { percentage: 90, tokens: 1e5 }
|
|
30596
|
+
}
|
|
30597
|
+
};
|
|
30598
|
+
};
|
|
28763
30599
|
const DEFAULT_SETTINGS = {
|
|
28764
30600
|
language: "en",
|
|
28765
30601
|
startupMode: ProjectStartMode.Empty,
|
|
@@ -28820,7 +30656,7 @@ const DEFAULT_SETTINGS = {
|
|
|
28820
30656
|
autoGenerateTaskName: true,
|
|
28821
30657
|
showTaskStateActions: true,
|
|
28822
30658
|
worktreeSymlinkFolders: ["node_modules", "vendor", "__pycache__", ".venv", "venv"],
|
|
28823
|
-
contextCompactingThreshold:
|
|
30659
|
+
contextCompactingThreshold: { percentage: 90, tokens: 1e5 },
|
|
28824
30660
|
contextCompactionType: ContextCompactionType.Compact,
|
|
28825
30661
|
defaultWorkingMode: "local",
|
|
28826
30662
|
worktreeBranchPrefix: "aider-desk/task/",
|
|
@@ -28833,12 +30669,13 @@ const DEFAULT_SETTINGS = {
|
|
|
28833
30669
|
proxy: {
|
|
28834
30670
|
enabled: false,
|
|
28835
30671
|
url: ""
|
|
28836
|
-
}
|
|
30672
|
+
},
|
|
30673
|
+
fileWatchMode: FileWatchMode.Auto
|
|
28837
30674
|
};
|
|
28838
30675
|
const compareBaseDirs = (baseDir1, baseDir2) => {
|
|
28839
30676
|
return normalizeBaseDir(baseDir1) === normalizeBaseDir(baseDir2);
|
|
28840
30677
|
};
|
|
28841
|
-
const CURRENT_SETTINGS_VERSION =
|
|
30678
|
+
const CURRENT_SETTINGS_VERSION = 20;
|
|
28842
30679
|
class Store {
|
|
28843
30680
|
// @ts-expect-error expected to be initialized
|
|
28844
30681
|
store;
|
|
@@ -28989,6 +30826,10 @@ class Store {
|
|
|
28989
30826
|
settings = migrateSettingsV18toV19(settings);
|
|
28990
30827
|
settingsVersion = 19;
|
|
28991
30828
|
}
|
|
30829
|
+
if (settingsVersion === 19) {
|
|
30830
|
+
settings = migrateSettingsV19toV20(settings);
|
|
30831
|
+
settingsVersion = 20;
|
|
30832
|
+
}
|
|
28992
30833
|
this.store.set("settings", settings);
|
|
28993
30834
|
this.store.set("openProjects", openProjects || []);
|
|
28994
30835
|
this.store.set("providers", providers || []);
|
|
@@ -29182,4 +31023,5 @@ if (!process.env.VITEST) {
|
|
|
29182
31023
|
}
|
|
29183
31024
|
exports.addProjectsFromEnv = addProjectsFromEnv;
|
|
29184
31025
|
exports.initializeLangfuseExporter = initializeLangfuseExporter;
|
|
31026
|
+
exports.initializePostHogExporter = initializePostHogExporter;
|
|
29185
31027
|
exports.logger = logger;
|