@aiderdesk/aiderdesk 0.64.0 → 0.66.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-BXuUWHai.js} +1 -1
- package/out/renderer/assets/{architectureDiagram-Q4EWVU46-B8_dgBXp.js → architectureDiagram-3BPJPVTR-Bm9oFTP_.js} +11 -9
- package/out/renderer/assets/{blockDiagram-DXYQGD6D-BDOvGPDN.js → blockDiagram-GPEHLZMM-DQ2DevZl.js} +218 -30
- package/out/renderer/assets/{c4Diagram-AHTNJAMY-1ABZnJ2v.js → c4Diagram-AAUBKEIU-BdR46VbA.js} +2 -2
- package/out/renderer/assets/{channel-Cr_H2zdE.js → channel-DUdp1NJ7.js} +1 -1
- package/out/renderer/assets/{chunk-EDXVE4YY-Dt80V_EG.js → chunk-2J33WTMH-Dzi-idRV.js} +1 -1
- package/out/renderer/assets/{chunk-4BX2VUAB-d88VZY9C.js → chunk-4BX2VUAB-DEyFpixF.js} +1 -1
- package/out/renderer/assets/{chunk-55IACEB6-BO1oJBQV.js → chunk-55IACEB6-DyyQMIZa.js} +1 -1
- package/out/renderer/assets/{chunk-4TB4RGXK-DLcMuHVI.js → chunk-727SXJPM-vXEiesCP.js} +245 -149
- package/out/renderer/assets/{chunk-OYMX7WX6-DBFhtMcs.js → chunk-AQP2D5EJ-B0SWmbrS.js} +92 -78
- package/out/renderer/assets/{chunk-FMBD7UC4-D5MNbIWZ.js → chunk-FMBD7UC4-CoPwvBCa.js} +1 -1
- package/out/renderer/assets/{chunk-YZCP3GAM-gAcMGuhT.js → chunk-ND2GUHAM-ku5t5SwP.js} +1 -1
- package/out/renderer/assets/{chunk-QZHKN3VN-Bxwt_pyh.js → chunk-QZHKN3VN-DBGBAqit.js} +1 -1
- package/out/renderer/assets/{classDiagram-6PBFFD2Q-B7lgamMP.js → classDiagram-4FO5ZUOK-DTyjsHX9.js} +6 -6
- package/out/renderer/assets/{classDiagram-v2-HSJHXN6E-B7lgamMP.js → classDiagram-v2-Q7XG4LA2-DTyjsHX9.js} +6 -6
- package/out/renderer/assets/{cose-bilkent-S5V4N54A-BZNBIG2x.js → cose-bilkent-S5V4N54A-wGfE9wIx.js} +1 -1
- package/out/renderer/assets/{dagre-KV5264BT-C3hXUNb-.js → dagre-BM42HDAG-cwyj1-l0.js} +17 -6
- package/out/renderer/assets/{diagram-MMDJMWI5-BcI1Ek4N.js → diagram-2AECGRRQ-BYvCxkOs.js} +3 -5
- package/out/renderer/assets/{diagram-5BDNPKRD-DNh45EqP.js → diagram-5GNKFQAL--ZlqvgmY.js} +4 -6
- package/out/renderer/assets/diagram-KO2AKTUF-CoPNrPhx.js +632 -0
- package/out/renderer/assets/{diagram-TYMM5635-DuHcW-s7.js → diagram-LMA3HP47-wjhxYTWf.js} +3 -5
- package/out/renderer/assets/{diagram-G4DWMVQ6-8lhqJfPk.js → diagram-OG6HWLK6-DhYpewbd.js} +4 -6
- package/out/renderer/assets/{erDiagram-SMLLAGMA-I6Q9HYdF.js → erDiagram-TEJ5UH35-DolRLBng.js} +4 -4
- package/out/renderer/assets/{flowDiagram-DWJPFMVM-BzRjtX5C.js → flowDiagram-I6XJVG4X-DjAbl_XC.js} +6 -6
- package/out/renderer/assets/{ganttDiagram-T4ZO3ILL-DVkem_IA.js → ganttDiagram-6RSMTGT7-AF7-XgtX.js} +7 -1
- package/out/renderer/assets/{gitGraphDiagram-UUTBAWPF-BYpvdMpK.js → gitGraphDiagram-PVQCEYII-BMZLakzH.js} +4 -6
- package/out/renderer/assets/{graph-CAtr5PoG.js → graph-B_ifajWk.js} +490 -135
- package/out/renderer/assets/{index-CNL53LoL.js → index-3bI-dJm8.js} +9752 -6469
- package/out/renderer/assets/{index-Duw36zwk.css → index-B62bIfbt.css} +107 -11
- package/out/renderer/assets/{infoDiagram-42DDH7IO-BcmBthOY.js → infoDiagram-5YYISTIA-0f7Qxxvp.js} +3 -5
- package/out/renderer/assets/{ishikawaDiagram-UXIWVN3A-moTWny-V.js → ishikawaDiagram-YF4QCWOH-BX_EIAMn.js} +1 -1
- package/out/renderer/assets/{journeyDiagram-VCZTEJTY-DOW8zaZt.js → journeyDiagram-JHISSGLW-Dmitv8wD.js} +4 -4
- package/out/renderer/assets/{kanban-definition-6JOO6SKY-DpJjTob4.js → kanban-definition-UN3LZRKU-By2GFUNB.js} +2 -2
- package/out/renderer/assets/{layout-BvH51Ui9.js → layout-DAkKffy1.js} +459 -32
- package/out/renderer/assets/{mindmap-definition-QFDTVHPH-DggFFNHq.js → mindmap-definition-RKZ34NQL-yIrV1m0y.js} +3 -3
- package/out/renderer/assets/{pieDiagram-DEJITSTG-BED4dnMF.js → pieDiagram-4H26LBE5-PV9y5rw_.js} +4 -6
- package/out/renderer/assets/{quadrantDiagram-34T5L4WZ-RpQ3qNU5.js → quadrantDiagram-W4KKPZXB-DeX0zTCp.js} +22 -20
- package/out/renderer/assets/{requirementDiagram-MS252O5E-VQt4zBMB.js → requirementDiagram-4Y6WPE33-Bzfk_KE-.js} +3 -3
- package/out/renderer/assets/{sankeyDiagram-XADWPNL6-DywR7qAk.js → sankeyDiagram-5OEKKPKP-BuCv8QIY.js} +80 -11
- package/out/renderer/assets/{sequenceDiagram-FGHM5R23-CVPfZD4e.js → sequenceDiagram-3UESZ5HK-Zg7Ukud8.js} +21 -9
- package/out/renderer/assets/{stateDiagram-FHFEXIEX-BrH8Q8ZG.js → stateDiagram-AJRCARHV-CLaqfYR8.js} +6 -8
- package/out/renderer/assets/{stateDiagram-v2-QKLJ7IA2-BTWk2K0H.js → stateDiagram-v2-BHNVJYJU-Cmm1ljQ4.js} +4 -4
- package/out/renderer/assets/{timeline-definition-GMOUNBTQ-DwDUCrTb.js → timeline-definition-PNZ67QCA-DQBaAVcC.js} +2 -2
- package/out/renderer/assets/{vennDiagram-DHZGUBPP-Bjvr7yGM.js → vennDiagram-CIIHVFJN-CuplbU_R.js} +1 -1
- package/out/renderer/assets/{wardley-RL74JXVD-Bo-sW7uQ.js → wardley-L42UT6IY-BiqfHMim.js} +25605 -19118
- package/out/renderer/assets/{wardleyDiagram-NUSXRM2D-DRW_1PCJ.js → wardleyDiagram-YWT4CUSO-BaV0FnUu.js} +112 -38
- package/out/renderer/assets/worker-CfJUABHG.js +12626 -0
- package/out/renderer/assets/{xychartDiagram-5P7HB3ND-Ds-qS4nC.js → xychartDiagram-2RQKCTM6-DA_Miw-n.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/runner.js +1711 -329
- package/package.json +29 -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");
|
|
@@ -234,7 +236,7 @@ const TASKS_TOOL_DESCRIPTIONS = {
|
|
|
234
236
|
[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
237
|
[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
238
|
[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
|
|
239
|
+
[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
240
|
[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
241
|
[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.",
|
|
240
242
|
[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."
|
|
@@ -297,7 +299,6 @@ const ProjectSettingsSchema = zod.z.object({
|
|
|
297
299
|
reasoningEffort: zod.z.string().optional(),
|
|
298
300
|
thinkingTokens: zod.z.string().optional(),
|
|
299
301
|
currentMode: zod.z.string(),
|
|
300
|
-
contextCompactingThreshold: zod.z.number().optional(),
|
|
301
302
|
weakModelLocked: zod.z.boolean().optional(),
|
|
302
303
|
autoApproveLocked: zod.z.boolean().optional(),
|
|
303
304
|
updatedFilesGroupMode: zod.z.enum(["grouped", "flat"]).default("flat"),
|
|
@@ -335,6 +336,7 @@ var MemoryEmbeddingProvider = /* @__PURE__ */ ((MemoryEmbeddingProvider2) => {
|
|
|
335
336
|
var ContextCompactionType = /* @__PURE__ */ ((ContextCompactionType2) => {
|
|
336
337
|
ContextCompactionType2["Compact"] = "compact";
|
|
337
338
|
ContextCompactionType2["Handoff"] = "handoff";
|
|
339
|
+
ContextCompactionType2["Smart"] = "smart";
|
|
338
340
|
return ContextCompactionType2;
|
|
339
341
|
})(ContextCompactionType || {});
|
|
340
342
|
var MemoryEmbeddingProgressPhase = /* @__PURE__ */ ((MemoryEmbeddingProgressPhase2) => {
|
|
@@ -380,7 +382,7 @@ const TaskDataSchema = zod.z.object({
|
|
|
380
382
|
reasoningEffort: zod.z.string().optional(),
|
|
381
383
|
thinkingTokens: zod.z.string().optional(),
|
|
382
384
|
currentMode: zod.z.string().optional(),
|
|
383
|
-
|
|
385
|
+
contextCompactingThresholdTokens: zod.z.number().optional(),
|
|
384
386
|
weakModelLocked: zod.z.boolean().optional(),
|
|
385
387
|
handoff: zod.z.boolean().optional(),
|
|
386
388
|
lastAgentProviderMetadata: zod.z.unknown().optional(),
|
|
@@ -455,6 +457,20 @@ const extractTextContent = (content) => {
|
|
|
455
457
|
}
|
|
456
458
|
return "";
|
|
457
459
|
};
|
|
460
|
+
const extractImagesFromContent = (content) => {
|
|
461
|
+
if (!Array.isArray(content)) {
|
|
462
|
+
return void 0;
|
|
463
|
+
}
|
|
464
|
+
const images = content.filter((part) => part.type === "image").map((part) => {
|
|
465
|
+
const data = part.image;
|
|
466
|
+
if (typeof data !== "string") {
|
|
467
|
+
return void 0;
|
|
468
|
+
}
|
|
469
|
+
const mediaType = part.mediaType || "image/png";
|
|
470
|
+
return data.startsWith("data:") ? data : `data:${mediaType};base64,${data}`;
|
|
471
|
+
}).filter((v) => v !== void 0);
|
|
472
|
+
return images.length > 0 ? images : void 0;
|
|
473
|
+
};
|
|
458
474
|
const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
459
475
|
const parseUsageReport = (model, report) => {
|
|
460
476
|
const sentMatch = report.match(/Tokens: ([\d.]+k?) sent/);
|
|
@@ -547,6 +563,7 @@ const extractProviderModel = (modelId) => {
|
|
|
547
563
|
const [providerId, ...modelParts] = modelId.split("/");
|
|
548
564
|
return [providerId, modelParts.join("/")];
|
|
549
565
|
};
|
|
566
|
+
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
567
|
const DEFAULT_PAGE_SIZE = 100;
|
|
551
568
|
class LogBuffer {
|
|
552
569
|
db = null;
|
|
@@ -793,6 +810,7 @@ const AIDER_DESK_CONNECTOR_DIR = path.join(AIDER_DESK_DATA_DIR, "aider-connector
|
|
|
793
810
|
const AIDER_DESK_MCP_SERVER_DIR = path.join(AIDER_DESK_DATA_DIR, "mcp-server");
|
|
794
811
|
const AIDER_DESK_BIN_DIR = path.join(AIDER_DESK_DATA_DIR, "bin");
|
|
795
812
|
const UV_EXECUTABLE = process.platform === "win32" ? path.join(AIDER_DESK_BIN_DIR, "uv.exe") : path.join(AIDER_DESK_BIN_DIR, "uv");
|
|
813
|
+
const RIPGREP_BINARY_PATH = process.platform === "win32" ? path.join(AIDER_DESK_BIN_DIR, "rg.exe") : path.join(AIDER_DESK_BIN_DIR, "rg");
|
|
796
814
|
const SERVER_PORT = process.env.AIDER_DESK_PORT ? parseInt(process.env.AIDER_DESK_PORT) : 24337;
|
|
797
815
|
const PID_FILES_DIR = path.join(AIDER_DESK_DATA_DIR, "aider-processes");
|
|
798
816
|
const AIDER_DESK_DIR = ".aider-desk";
|
|
@@ -816,7 +834,9 @@ const AIDER_DESK_MEMORY_FILE = path.join(AIDER_DESK_DATA_DIR, "memory.db");
|
|
|
816
834
|
const EXTENSIONS_REPOS_CACHE_DIR = path.join(AIDER_DESK_CACHE_DIR, "extensions");
|
|
817
835
|
const POSTHOG_PUBLIC_API_KEY = "phc_AF4zkjrcziXLh8PBFsRSvVr4VZ38p3ezsdX0KDYuElI";
|
|
818
836
|
const POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
819
|
-
process.env.AIDER_DESK_HEADLESS === "true";
|
|
837
|
+
const HEADLESS_MODE = process.env.AIDER_DESK_HEADLESS === "true";
|
|
838
|
+
const APP_TYPE = process.env.AIDER_DESK_APP_TYPE || (HEADLESS_MODE ? "docker" : "electron");
|
|
839
|
+
process.env.AIDER_DESK_DISABLE_MENU === "true";
|
|
820
840
|
const AUTH_USERNAME = process.env.AIDER_DESK_USERNAME;
|
|
821
841
|
const AUTH_PASSWORD = process.env.AIDER_DESK_PASSWORD;
|
|
822
842
|
const CORS_ALLOWED_ORIGINS = process.env.AIDER_DESK_CORS_ALLOWED_ORIGINS;
|
|
@@ -875,7 +895,7 @@ class ApprovalManager {
|
|
|
875
895
|
async handleToolApproval(toolName, input, key, text, subject) {
|
|
876
896
|
const extensionResult = await this.task.dispatchExtensionEvent("onToolApproval", { toolName, input });
|
|
877
897
|
if (extensionResult.blocked) {
|
|
878
|
-
return [false, void 0];
|
|
898
|
+
return [false, typeof extensionResult.blocked === "string" ? extensionResult.blocked : void 0];
|
|
879
899
|
}
|
|
880
900
|
if (extensionResult.allowed) {
|
|
881
901
|
return [true, void 0];
|
|
@@ -1548,7 +1568,7 @@ const DEFAULT_AGENT_PROFILE = {
|
|
|
1548
1568
|
name: "Default Agent",
|
|
1549
1569
|
provider: "anthropic",
|
|
1550
1570
|
model: DEFAULT_PROVIDER_MODELS.anthropic,
|
|
1551
|
-
maxIterations:
|
|
1571
|
+
maxIterations: 0,
|
|
1552
1572
|
minTimeBetweenToolCalls: 0,
|
|
1553
1573
|
toolApprovals: {
|
|
1554
1574
|
// aider tools
|
|
@@ -1869,7 +1889,8 @@ const getDefaultProviderParams = (providerName) => {
|
|
|
1869
1889
|
name: "openai-compatible",
|
|
1870
1890
|
apiKey: "",
|
|
1871
1891
|
baseUrl: "",
|
|
1872
|
-
reasoningEffort: ReasoningEffort.None
|
|
1892
|
+
reasoningEffort: ReasoningEffort.None,
|
|
1893
|
+
trackTokenUsage: true
|
|
1873
1894
|
};
|
|
1874
1895
|
break;
|
|
1875
1896
|
case "litellm":
|
|
@@ -1984,7 +2005,7 @@ class TelemetryManager {
|
|
|
1984
2005
|
}
|
|
1985
2006
|
async init() {
|
|
1986
2007
|
try {
|
|
1987
|
-
await Promise.resolve().then(() => require("./open-telemetry-
|
|
2008
|
+
await Promise.resolve().then(() => require("./open-telemetry-baOvr6sK.js"));
|
|
1988
2009
|
const app = getElectronApp();
|
|
1989
2010
|
this.client = new posthogNode.PostHog(POSTHOG_PUBLIC_API_KEY, {
|
|
1990
2011
|
host: POSTHOG_HOST
|
|
@@ -1994,7 +2015,8 @@ class TelemetryManager {
|
|
|
1994
2015
|
distinctId: this.distinctId,
|
|
1995
2016
|
properties: {
|
|
1996
2017
|
os: process.platform,
|
|
1997
|
-
version: app?.getVersion()
|
|
2018
|
+
version: app?.getVersion(),
|
|
2019
|
+
appType: APP_TYPE
|
|
1998
2020
|
}
|
|
1999
2021
|
});
|
|
2000
2022
|
} catch (error) {
|
|
@@ -2157,6 +2179,24 @@ const getLangfuseEnvironmentVariables = (baseDir, settings) => {
|
|
|
2157
2179
|
LANGFUSE_HOST: getEffectiveEnvironmentVariable("LANGFUSE_HOST", settings, baseDir)?.value
|
|
2158
2180
|
};
|
|
2159
2181
|
};
|
|
2182
|
+
const initializePostHogExporter = () => {
|
|
2183
|
+
const posthogApiKey = getEffectiveEnvironmentVariable("POSTHOG_API_KEY");
|
|
2184
|
+
const posthogHost = getEffectiveEnvironmentVariable("POSTHOG_HOST");
|
|
2185
|
+
if (posthogApiKey) {
|
|
2186
|
+
logger.info("Initializing PostHog Trace Exporter...");
|
|
2187
|
+
return new otel.PostHogTraceExporter({
|
|
2188
|
+
apiKey: posthogApiKey.value,
|
|
2189
|
+
host: posthogHost?.value || "https://us.i.posthog.com"
|
|
2190
|
+
});
|
|
2191
|
+
}
|
|
2192
|
+
return void 0;
|
|
2193
|
+
};
|
|
2194
|
+
const getPostHogAiderEnvironmentVariables = (baseDir, settings) => {
|
|
2195
|
+
return {
|
|
2196
|
+
POSTHOG_API_KEY: getEffectiveEnvironmentVariable("POSTHOG_API_KEY", settings, baseDir)?.value,
|
|
2197
|
+
POSTHOG_API_URL: getEffectiveEnvironmentVariable("POSTHOG_HOST", settings, baseDir)?.value
|
|
2198
|
+
};
|
|
2199
|
+
};
|
|
2160
2200
|
const readEnvFile = (filePath) => {
|
|
2161
2201
|
try {
|
|
2162
2202
|
if (fs$1.existsSync(filePath)) {
|
|
@@ -2353,7 +2393,8 @@ const getEnvironmentVariablesForAider = (settings, baseDir) => {
|
|
|
2353
2393
|
};
|
|
2354
2394
|
const getTelemetryEnvironmentVariablesForAider = (settings, baseDir) => {
|
|
2355
2395
|
return {
|
|
2356
|
-
...getLangfuseEnvironmentVariables(baseDir, settings)
|
|
2396
|
+
...getLangfuseEnvironmentVariables(baseDir, settings),
|
|
2397
|
+
...getPostHogAiderEnvironmentVariables(baseDir, settings)
|
|
2357
2398
|
};
|
|
2358
2399
|
};
|
|
2359
2400
|
const getDefaultProjectSettings = (store, providerModels, baseDir, defaultAgentProfileId = DEFAULT_AGENT_PROFILE.id, providers) => {
|
|
@@ -2843,8 +2884,8 @@ const downloadUv = async () => {
|
|
|
2843
2884
|
strip: 1
|
|
2844
2885
|
});
|
|
2845
2886
|
} else if (filename.endsWith(".zip")) {
|
|
2846
|
-
const
|
|
2847
|
-
const zip = new
|
|
2887
|
+
const AdmZip2 = (await import("adm-zip")).default;
|
|
2888
|
+
const zip = new AdmZip2(tempFile);
|
|
2848
2889
|
zip.extractAllTo(AIDER_DESK_BIN_DIR, true);
|
|
2849
2890
|
}
|
|
2850
2891
|
if (platform !== "win32") {
|
|
@@ -2907,6 +2948,111 @@ const getLatestPythonLibVersion = async (library) => {
|
|
|
2907
2948
|
return null;
|
|
2908
2949
|
}
|
|
2909
2950
|
};
|
|
2951
|
+
const RIPGREP_VERSION = "15.1.0";
|
|
2952
|
+
const RIPGREP_BASE_URL = `https://github.com/BurntSushi/ripgrep/releases/download/${RIPGREP_VERSION}`;
|
|
2953
|
+
const getRipgrepAssetName = () => {
|
|
2954
|
+
const platform = process.platform;
|
|
2955
|
+
const arch = process.arch;
|
|
2956
|
+
if (platform === "win32") {
|
|
2957
|
+
return arch === "arm64" ? `ripgrep-${RIPGREP_VERSION}-aarch64-pc-windows-msvc.zip` : `ripgrep-${RIPGREP_VERSION}-x86_64-pc-windows-msvc.zip`;
|
|
2958
|
+
}
|
|
2959
|
+
if (platform === "darwin") {
|
|
2960
|
+
return arch === "arm64" ? `ripgrep-${RIPGREP_VERSION}-aarch64-apple-darwin.tar.gz` : `ripgrep-${RIPGREP_VERSION}-x86_64-apple-darwin.tar.gz`;
|
|
2961
|
+
}
|
|
2962
|
+
if (arch === "arm64") {
|
|
2963
|
+
return `ripgrep-${RIPGREP_VERSION}-aarch64-unknown-linux-gnu.tar.gz`;
|
|
2964
|
+
}
|
|
2965
|
+
return `ripgrep-${RIPGREP_VERSION}-x86_64-unknown-linux-musl.tar.gz`;
|
|
2966
|
+
};
|
|
2967
|
+
const isRipgrepAvailable = () => {
|
|
2968
|
+
try {
|
|
2969
|
+
if (!fs$1.existsSync(RIPGREP_BINARY_PATH)) {
|
|
2970
|
+
return false;
|
|
2971
|
+
}
|
|
2972
|
+
const result = child_process.execSync(`"${RIPGREP_BINARY_PATH}" --version`, {
|
|
2973
|
+
encoding: "utf8",
|
|
2974
|
+
timeout: 5e3,
|
|
2975
|
+
windowsHide: true
|
|
2976
|
+
});
|
|
2977
|
+
if (!result.includes("ripgrep")) {
|
|
2978
|
+
return false;
|
|
2979
|
+
}
|
|
2980
|
+
logger.debug(`ripgrep is available at ${RIPGREP_BINARY_PATH}: ${result.trim()}`);
|
|
2981
|
+
return true;
|
|
2982
|
+
} catch {
|
|
2983
|
+
return false;
|
|
2984
|
+
}
|
|
2985
|
+
};
|
|
2986
|
+
const downloadRipgrep = async () => {
|
|
2987
|
+
const filename = getRipgrepAssetName();
|
|
2988
|
+
const url = `${RIPGREP_BASE_URL}/${filename}`;
|
|
2989
|
+
if (!fs$1.existsSync(AIDER_DESK_BIN_DIR)) {
|
|
2990
|
+
fs$1.mkdirSync(AIDER_DESK_BIN_DIR, { recursive: true });
|
|
2991
|
+
}
|
|
2992
|
+
const tempFile = path.join(AIDER_DESK_BIN_DIR, filename);
|
|
2993
|
+
const rgExeName = process.platform === "win32" ? "rg.exe" : "rg";
|
|
2994
|
+
try {
|
|
2995
|
+
logger.info(`Downloading ripgrep from ${url}`);
|
|
2996
|
+
const response = await fetch(url);
|
|
2997
|
+
if (!response.ok) {
|
|
2998
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
2999
|
+
}
|
|
3000
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
3001
|
+
fs$1.writeFileSync(tempFile, buffer);
|
|
3002
|
+
if (filename.endsWith(".tar.gz")) {
|
|
3003
|
+
const { extract } = await import("tar");
|
|
3004
|
+
await extract({
|
|
3005
|
+
cwd: AIDER_DESK_BIN_DIR,
|
|
3006
|
+
file: tempFile,
|
|
3007
|
+
strip: 1
|
|
3008
|
+
});
|
|
3009
|
+
} else if (filename.endsWith(".zip")) {
|
|
3010
|
+
const zip = new AdmZip(tempFile);
|
|
3011
|
+
const entry = zip.getEntry(rgExeName) || zip.getEntries().find((e) => e.entryName.endsWith(rgExeName));
|
|
3012
|
+
if (entry) {
|
|
3013
|
+
fs$1.writeFileSync(path.join(AIDER_DESK_BIN_DIR, rgExeName), entry.getData());
|
|
3014
|
+
} else {
|
|
3015
|
+
throw new Error(`Could not find ${rgExeName} in zip archive`);
|
|
3016
|
+
}
|
|
3017
|
+
}
|
|
3018
|
+
if (process.platform !== "win32") {
|
|
3019
|
+
fs$1.chmodSync(path.join(AIDER_DESK_BIN_DIR, rgExeName), 493);
|
|
3020
|
+
}
|
|
3021
|
+
logger.info(`ripgrep downloaded successfully to ${AIDER_DESK_BIN_DIR}`);
|
|
3022
|
+
} finally {
|
|
3023
|
+
if (fs$1.existsSync(tempFile)) {
|
|
3024
|
+
fs$1.unlinkSync(tempFile);
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
};
|
|
3028
|
+
let ripgrepEnsured = false;
|
|
3029
|
+
let ripgrepEnsurePromise = null;
|
|
3030
|
+
const ensureRipgrepBinary = async () => {
|
|
3031
|
+
if (ripgrepEnsured) {
|
|
3032
|
+
return true;
|
|
3033
|
+
}
|
|
3034
|
+
if (!ripgrepEnsurePromise) {
|
|
3035
|
+
ripgrepEnsurePromise = (async () => {
|
|
3036
|
+
if (isRipgrepAvailable()) {
|
|
3037
|
+
ripgrepEnsured = true;
|
|
3038
|
+
return;
|
|
3039
|
+
}
|
|
3040
|
+
await downloadRipgrep();
|
|
3041
|
+
if (!isRipgrepAvailable()) {
|
|
3042
|
+
throw new Error("ripgrep binary not found after download");
|
|
3043
|
+
}
|
|
3044
|
+
ripgrepEnsured = true;
|
|
3045
|
+
})();
|
|
3046
|
+
}
|
|
3047
|
+
try {
|
|
3048
|
+
await ripgrepEnsurePromise;
|
|
3049
|
+
return true;
|
|
3050
|
+
} catch (error) {
|
|
3051
|
+
ripgrepEnsurePromise = null;
|
|
3052
|
+
logger.error("Failed to ensure ripgrep binary:", error);
|
|
3053
|
+
return false;
|
|
3054
|
+
}
|
|
3055
|
+
};
|
|
2910
3056
|
const isAbortError = (error) => {
|
|
2911
3057
|
if (!(error instanceof Error)) {
|
|
2912
3058
|
return false;
|
|
@@ -3078,18 +3224,25 @@ const expandTilde = (filePath) => {
|
|
|
3078
3224
|
}
|
|
3079
3225
|
return filePath;
|
|
3080
3226
|
};
|
|
3081
|
-
const readFileContent = async (absolutePath, withLines = false, lineOffset = 0, lineLimit = 1e3, sizeLimit = 0.05 * lineLimit) => {
|
|
3227
|
+
const readFileContent = async (absolutePath, withLines = false, lineOffset = 0, lineLimit = 1e3, sizeLimit = Math.max(50, 0.05 * lineLimit)) => {
|
|
3082
3228
|
const fileContentBuffer = await fs.readFile(absolutePath);
|
|
3083
3229
|
if (istextorbinary.isBinary(absolutePath, fileContentBuffer)) {
|
|
3084
3230
|
throw new Error("Binary files cannot be read.");
|
|
3085
3231
|
}
|
|
3086
|
-
const
|
|
3087
|
-
|
|
3088
|
-
|
|
3232
|
+
const fileContent = fileContentBuffer.toString("utf8");
|
|
3233
|
+
const lines = fileContent.split("\n");
|
|
3234
|
+
const totalLines = lines.length;
|
|
3235
|
+
const startIndex = Math.max(0, lineOffset);
|
|
3236
|
+
const endIndex = Math.min(totalLines, startIndex + lineLimit);
|
|
3237
|
+
const limitedLines = lines.slice(startIndex, endIndex);
|
|
3238
|
+
const limitedContent = limitedLines.join("\n");
|
|
3239
|
+
const limitedSizeKB = Buffer.byteLength(limitedContent, "utf8") / 1024;
|
|
3240
|
+
if (limitedSizeKB > sizeLimit) {
|
|
3241
|
+
const truncatedBytes = Buffer.from(limitedContent, "utf8").subarray(0, Math.floor(sizeLimit * 1024));
|
|
3089
3242
|
const truncatedContent = truncatedBytes.toString("utf8");
|
|
3090
3243
|
const truncatedLines = truncatedContent.split("\n");
|
|
3091
3244
|
if (withLines) {
|
|
3092
|
-
return truncatedLines.map((line, index) => `${index + 1}|${line}`).join("\n") + `
|
|
3245
|
+
return truncatedLines.map((line, index) => `${startIndex + index + 1}|${line}`).join("\n") + `
|
|
3093
3246
|
|
|
3094
3247
|
File size limit (${sizeLimit.toFixed(1)} KB) exceeded. Use shell commands (e.g., head, tail, grep) to read specific parts.`;
|
|
3095
3248
|
}
|
|
@@ -3097,33 +3250,27 @@ File size limit (${sizeLimit.toFixed(1)} KB) exceeded. Use shell commands (e.g.,
|
|
|
3097
3250
|
|
|
3098
3251
|
File size limit (${sizeLimit.toFixed(1)} KB) exceeded. Use shell commands (e.g., head, tail, grep) to read specific parts.`;
|
|
3099
3252
|
}
|
|
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);
|
|
3253
|
+
let resultLines = limitedLines;
|
|
3106
3254
|
if (withLines) {
|
|
3107
|
-
|
|
3255
|
+
resultLines = limitedLines.map((line, index) => `${startIndex + index + 1}|${line}`);
|
|
3108
3256
|
}
|
|
3109
3257
|
if (endIndex < totalLines) {
|
|
3110
|
-
|
|
3111
|
-
limitedLines.push(`Total lines in the file: ${totalLines}`);
|
|
3258
|
+
resultLines = [...resultLines, "...", `Total lines in the file: ${totalLines}`];
|
|
3112
3259
|
}
|
|
3113
|
-
return
|
|
3260
|
+
return resultLines.join("\n");
|
|
3114
3261
|
};
|
|
3115
|
-
const truncateToolResult = async (content, maxLines = 1e3, maxSizeKB = 50) => {
|
|
3262
|
+
const truncateToolResult = async (content, maxLines = 1e3, maxSizeKB = 50, maxTokens = 5e4) => {
|
|
3116
3263
|
const lines = content.split("\n");
|
|
3117
3264
|
const sizeBytes = Buffer.byteLength(content, "utf8");
|
|
3118
3265
|
const sizeKB = sizeBytes / 1024;
|
|
3119
|
-
|
|
3266
|
+
const tokenCount = maxTokens === Infinity ? 0 : gpt4o.encode(content).length;
|
|
3267
|
+
if (lines.length <= maxLines && sizeKB <= maxSizeKB && tokenCount <= maxTokens) {
|
|
3120
3268
|
return content;
|
|
3121
3269
|
}
|
|
3122
3270
|
const id = Date.now().toString(36) + Math.random().toString(36).substring(2, 8);
|
|
3123
3271
|
const tmpFileName = `aider-desk-tool-result-${id}.txt`;
|
|
3124
3272
|
const tmpFilePath = path.join(os.tmpdir(), tmpFileName);
|
|
3125
3273
|
await fs.writeFile(tmpFilePath, content, "utf8");
|
|
3126
|
-
const previewLines = lines.slice(0, maxLines);
|
|
3127
3274
|
const reasons = [];
|
|
3128
3275
|
if (lines.length > maxLines) {
|
|
3129
3276
|
reasons.push(`${lines.length} lines exceeded limit of ${maxLines}`);
|
|
@@ -3131,9 +3278,126 @@ const truncateToolResult = async (content, maxLines = 1e3, maxSizeKB = 50) => {
|
|
|
3131
3278
|
if (sizeKB > maxSizeKB) {
|
|
3132
3279
|
reasons.push(`${sizeKB.toFixed(1)} KB exceeded limit of ${maxSizeKB} KB`);
|
|
3133
3280
|
}
|
|
3134
|
-
|
|
3281
|
+
if (tokenCount > maxTokens) {
|
|
3282
|
+
reasons.push(`${tokenCount} tokens exceeded limit of ${maxTokens}`);
|
|
3283
|
+
}
|
|
3284
|
+
if (tokenCount > maxTokens) {
|
|
3285
|
+
const headBudget = Math.floor(maxTokens / 2);
|
|
3286
|
+
const tailBudget = maxTokens - headBudget;
|
|
3287
|
+
const headLines = [];
|
|
3288
|
+
let headTokens = 0;
|
|
3289
|
+
for (const line of lines) {
|
|
3290
|
+
const lineTokens = gpt4o.encode(line).length;
|
|
3291
|
+
if (headTokens + lineTokens > headBudget) {
|
|
3292
|
+
break;
|
|
3293
|
+
}
|
|
3294
|
+
headLines.push(line);
|
|
3295
|
+
headTokens += lineTokens;
|
|
3296
|
+
}
|
|
3297
|
+
const tailLines = [];
|
|
3298
|
+
let tailTokens = 0;
|
|
3299
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
3300
|
+
if (headLines.length + tailLines.length >= lines.length) {
|
|
3301
|
+
break;
|
|
3302
|
+
}
|
|
3303
|
+
const lineTokens = gpt4o.encode(lines[i]).length;
|
|
3304
|
+
if (tailTokens + lineTokens > tailBudget) {
|
|
3305
|
+
break;
|
|
3306
|
+
}
|
|
3307
|
+
tailLines.unshift(lines[i]);
|
|
3308
|
+
tailTokens += lineTokens;
|
|
3309
|
+
}
|
|
3310
|
+
const omittedLines = lines.length - headLines.length - tailLines.length;
|
|
3311
|
+
const truncationNotice = `
|
|
3312
|
+
|
|
3313
|
+
... ${omittedLines} lines omitted (${reasons.join(", ")}). Full content saved to ${tmpFilePath}.
|
|
3314
|
+
|
|
3315
|
+
`;
|
|
3316
|
+
return headLines.join("\n") + truncationNotice + tailLines.join("\n");
|
|
3317
|
+
}
|
|
3318
|
+
let preview;
|
|
3319
|
+
if (sizeKB > maxSizeKB) {
|
|
3320
|
+
const maxBytes = Math.floor(maxSizeKB * 1024);
|
|
3321
|
+
const contentBuffer = Buffer.from(content, "utf8");
|
|
3322
|
+
preview = contentBuffer.subarray(0, maxBytes).toString("utf8");
|
|
3323
|
+
} else {
|
|
3324
|
+
preview = lines.slice(0, maxLines).join("\n");
|
|
3325
|
+
}
|
|
3326
|
+
return preview + `
|
|
3135
3327
|
... Content truncated (${reasons.join(", ")}). Full content saved to ${tmpFilePath}.`;
|
|
3136
3328
|
};
|
|
3329
|
+
const NETWORK_ERROR_CODES = ["ECONNRESET", "EPIPE", "ETIMEDOUT", "ECONNREFUSED", "ENOTFOUND", "ENETUNREACH", "EAI_AGAIN"];
|
|
3330
|
+
const UNDICI_ERROR_PREFIX = "UND_ERR_";
|
|
3331
|
+
const isNetworkError = (error) => {
|
|
3332
|
+
if (!(error instanceof Error)) {
|
|
3333
|
+
return false;
|
|
3334
|
+
}
|
|
3335
|
+
if (error instanceof TypeError && error.message === "terminated") {
|
|
3336
|
+
return true;
|
|
3337
|
+
}
|
|
3338
|
+
if ("code" in error) {
|
|
3339
|
+
const code = error.code;
|
|
3340
|
+
if (typeof code === "string") {
|
|
3341
|
+
if (code.startsWith(UNDICI_ERROR_PREFIX)) {
|
|
3342
|
+
return true;
|
|
3343
|
+
}
|
|
3344
|
+
if (NETWORK_ERROR_CODES.includes(code)) {
|
|
3345
|
+
return true;
|
|
3346
|
+
}
|
|
3347
|
+
}
|
|
3348
|
+
}
|
|
3349
|
+
if (error.cause instanceof Error && isNetworkError(error.cause)) {
|
|
3350
|
+
return true;
|
|
3351
|
+
}
|
|
3352
|
+
return false;
|
|
3353
|
+
};
|
|
3354
|
+
const IMAGE_TOKEN_ESTIMATE = 1e3;
|
|
3355
|
+
const extractToolResultText = (output) => {
|
|
3356
|
+
if (!output || typeof output !== "object") {
|
|
3357
|
+
return "";
|
|
3358
|
+
}
|
|
3359
|
+
const o = output;
|
|
3360
|
+
if (o.type === "text" || o.type === "error-text") {
|
|
3361
|
+
return String(o.value ?? "");
|
|
3362
|
+
}
|
|
3363
|
+
if (o.type === "json" || o.type === "error-json") {
|
|
3364
|
+
return JSON.stringify(o.value);
|
|
3365
|
+
}
|
|
3366
|
+
if (o.type === "content" && Array.isArray(o.value)) {
|
|
3367
|
+
return o.value.map((v) => v.type === "text" ? v.text ?? "" : v.type === "media" ? "[media]" : "").join("\n");
|
|
3368
|
+
}
|
|
3369
|
+
return JSON.stringify(output);
|
|
3370
|
+
};
|
|
3371
|
+
const estimateMessageTokens = (messages) => {
|
|
3372
|
+
let estimatedImageTokens = 0;
|
|
3373
|
+
const textOnlyMessages = messages.map((msg) => {
|
|
3374
|
+
if (typeof msg.content === "string") {
|
|
3375
|
+
return msg;
|
|
3376
|
+
}
|
|
3377
|
+
if (!Array.isArray(msg.content)) {
|
|
3378
|
+
return { role: msg.role, content: "" };
|
|
3379
|
+
}
|
|
3380
|
+
const parts = msg.content;
|
|
3381
|
+
const textParts = [];
|
|
3382
|
+
for (const part of parts) {
|
|
3383
|
+
const type = part.type;
|
|
3384
|
+
if (type === "text" && typeof part.text === "string") {
|
|
3385
|
+
textParts.push(part.text);
|
|
3386
|
+
} else if (type === "tool-call") {
|
|
3387
|
+
textParts.push(JSON.stringify(part.input ?? ""));
|
|
3388
|
+
} else if (type === "tool-result") {
|
|
3389
|
+
textParts.push(extractToolResultText(part.output));
|
|
3390
|
+
} else if (type === "reasoning" && typeof part.text === "string") {
|
|
3391
|
+
textParts.push(part.text);
|
|
3392
|
+
} else if (type === "image") {
|
|
3393
|
+
estimatedImageTokens += IMAGE_TOKEN_ESTIMATE;
|
|
3394
|
+
}
|
|
3395
|
+
}
|
|
3396
|
+
return { role: msg.role, content: textParts.join("\n\n") };
|
|
3397
|
+
});
|
|
3398
|
+
return textOnlyMessages.reduce((sum, msg) => sum + gpt4o.encode(typeof msg.content === "string" ? msg.content : "").length, 0) + estimatedImageTokens;
|
|
3399
|
+
};
|
|
3400
|
+
const execFileAsync = util.promisify(child_process.execFile);
|
|
3137
3401
|
const fileLocks = /* @__PURE__ */ new Map();
|
|
3138
3402
|
const withFileLock = (filePath, operation) => {
|
|
3139
3403
|
logger.debug("Acquiring file lock:", { filePath });
|
|
@@ -3445,50 +3709,80 @@ Do not use escape characters \\ in the string like \\n or \\" and others. Do not
|
|
|
3445
3709
|
if (!isApproved) {
|
|
3446
3710
|
return `Grep search for '${searchTerm}' in files matching '${filePattern}' denied by user. Reason: ${userInput}`;
|
|
3447
3711
|
}
|
|
3712
|
+
const rgAvailable = await ensureRipgrepBinary();
|
|
3713
|
+
if (!rgAvailable) {
|
|
3714
|
+
return "Error: ripgrep binary is not available. Please try again or check the logs for details.";
|
|
3715
|
+
}
|
|
3448
3716
|
try {
|
|
3449
|
-
const
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3717
|
+
const rgArgs = ["--no-heading", "--line-number", "--color", "never", "--max-count", String(maxResults)];
|
|
3718
|
+
if (!caseSensitive) {
|
|
3719
|
+
rgArgs.push("-i");
|
|
3720
|
+
}
|
|
3721
|
+
if (contextLines > 0) {
|
|
3722
|
+
rgArgs.push("-C", String(contextLines));
|
|
3723
|
+
}
|
|
3724
|
+
rgArgs.push("-g", filePattern);
|
|
3725
|
+
rgArgs.push("--", searchTerm, ".");
|
|
3726
|
+
const taskDir = task.getTaskDir();
|
|
3727
|
+
logger.debug("Executing ripgrep:", {
|
|
3728
|
+
binary: RIPGREP_BINARY_PATH,
|
|
3729
|
+
args: rgArgs,
|
|
3730
|
+
cwd: taskDir
|
|
3454
3731
|
});
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3732
|
+
const { stdout, stderr } = await execFileAsync(RIPGREP_BINARY_PATH, rgArgs, {
|
|
3733
|
+
cwd: taskDir,
|
|
3734
|
+
env: { ...process.env, TERM: "dumb", PATH: getShellPath() },
|
|
3735
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
3736
|
+
windowsHide: true
|
|
3737
|
+
});
|
|
3738
|
+
logger.debug("ripgrep stdout raw:", { stdout: stdout.substring(0, 2e3) });
|
|
3739
|
+
logger.debug("ripgrep stderr:", { stderr: stderr.substring(0, 2e3) });
|
|
3740
|
+
const outputLines = stdout.split("\n").filter(Boolean);
|
|
3741
|
+
logger.debug("outputLines count:", { count: outputLines.length, lines: outputLines.slice(0, 20) });
|
|
3742
|
+
if (outputLines.length === 0) {
|
|
3743
|
+
return `No matches found for pattern '${searchTerm}' in files matching '${filePattern}'.`;
|
|
3461
3744
|
}
|
|
3462
3745
|
const results = [];
|
|
3463
|
-
const
|
|
3464
|
-
for (
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
const
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3746
|
+
const outputLineRegex = /^(.+?)([:-])(\d+)\2(.*)$/;
|
|
3747
|
+
for (let rawLine of outputLines) {
|
|
3748
|
+
if (rawLine.endsWith("\r")) {
|
|
3749
|
+
rawLine = rawLine.slice(0, -1);
|
|
3750
|
+
}
|
|
3751
|
+
const line = rawLine;
|
|
3752
|
+
const match = outputLineRegex.exec(line);
|
|
3753
|
+
if (!match) {
|
|
3754
|
+
continue;
|
|
3755
|
+
}
|
|
3756
|
+
const [, rawFilePath, separator, lineNumStr, content] = match;
|
|
3757
|
+
const lineNum = parseInt(lineNumStr, 10);
|
|
3758
|
+
if (isNaN(lineNum)) {
|
|
3759
|
+
continue;
|
|
3760
|
+
}
|
|
3761
|
+
const filePath = rawFilePath.startsWith("./") ? rawFilePath.slice(2) : rawFilePath;
|
|
3762
|
+
const isMatch = separator === ":";
|
|
3763
|
+
if (isMatch) {
|
|
3764
|
+
results.push({
|
|
3765
|
+
filePath,
|
|
3766
|
+
lineNumber: lineNum,
|
|
3767
|
+
lineContent: content
|
|
3768
|
+
});
|
|
3769
|
+
} else {
|
|
3770
|
+
const lastResult = results[results.length - 1];
|
|
3771
|
+
if (lastResult) {
|
|
3772
|
+
if (!lastResult.context) {
|
|
3773
|
+
lastResult.context = [];
|
|
3486
3774
|
}
|
|
3487
|
-
|
|
3775
|
+
lastResult.context.push(content);
|
|
3488
3776
|
}
|
|
3489
3777
|
}
|
|
3778
|
+
if (results.length >= maxResults) {
|
|
3779
|
+
break;
|
|
3780
|
+
}
|
|
3490
3781
|
}
|
|
3491
3782
|
if (results.length === 0) {
|
|
3783
|
+
if (stderr.trim()) {
|
|
3784
|
+
return `Error during grep: ${stderr.trim()}`;
|
|
3785
|
+
}
|
|
3492
3786
|
return `No matches found for pattern '${searchTerm}' in files matching '${filePattern}'.`;
|
|
3493
3787
|
}
|
|
3494
3788
|
const grouped = {};
|
|
@@ -3498,33 +3792,33 @@ Do not use escape characters \\ in the string like \\n or \\" and others. Do not
|
|
|
3498
3792
|
}
|
|
3499
3793
|
grouped[r.filePath].push(r);
|
|
3500
3794
|
}
|
|
3501
|
-
const
|
|
3502
|
-
|
|
3503
|
-
|
|
3795
|
+
const markdownLines = [];
|
|
3796
|
+
markdownLines.push(`## Grep Results: \`${searchTerm}\` in \`${filePattern}\` (${results.length} matches)`);
|
|
3797
|
+
markdownLines.push("");
|
|
3504
3798
|
for (const [filePath, matches] of Object.entries(grouped)) {
|
|
3505
|
-
|
|
3799
|
+
markdownLines.push(`### ${filePath} (${matches.length} ${matches.length === 1 ? "match" : "matches"})`);
|
|
3506
3800
|
for (const match of matches) {
|
|
3507
3801
|
const escapedContent = match.lineContent.replace(/`/g, "\\`");
|
|
3508
|
-
|
|
3802
|
+
markdownLines.push(`- **L${match.lineNumber}:** \`${escapedContent}\``);
|
|
3509
3803
|
if (match.context && match.context.length > 0) {
|
|
3510
|
-
|
|
3804
|
+
markdownLines.push(" ```");
|
|
3511
3805
|
for (const ctxLine of match.context) {
|
|
3512
|
-
|
|
3806
|
+
markdownLines.push(` ${ctxLine}`);
|
|
3513
3807
|
}
|
|
3514
|
-
|
|
3808
|
+
markdownLines.push(" ```");
|
|
3515
3809
|
}
|
|
3516
3810
|
}
|
|
3517
|
-
|
|
3811
|
+
markdownLines.push("");
|
|
3518
3812
|
}
|
|
3519
3813
|
const notices = [];
|
|
3520
3814
|
if (results.length >= maxResults) {
|
|
3521
3815
|
notices.push(`${maxResults} matches limit reached. Use maxResults=${maxResults * 2} for more, or refine pattern`);
|
|
3522
3816
|
}
|
|
3523
3817
|
if (notices.length > 0) {
|
|
3524
|
-
|
|
3525
|
-
|
|
3818
|
+
markdownLines.push("---");
|
|
3819
|
+
markdownLines.push(`[${notices.join(". ")}]`);
|
|
3526
3820
|
}
|
|
3527
|
-
return
|
|
3821
|
+
return await truncateToolResult(markdownLines.join("\n"), 1e3, 50, 1e4);
|
|
3528
3822
|
} catch (error) {
|
|
3529
3823
|
if (isAbortError(error)) {
|
|
3530
3824
|
return "Operation was cancelled by user.";
|
|
@@ -3581,6 +3875,18 @@ Timeout: ${timeout}ms`;
|
|
|
3581
3875
|
return `Bash command execution denied by user. Reason: ${userInput}`;
|
|
3582
3876
|
}
|
|
3583
3877
|
const absoluteCwd = expandedCwd ? path.resolve(task.getTaskDir(), expandedCwd) : task.getTaskDir();
|
|
3878
|
+
const isPosix = process.platform !== "win32";
|
|
3879
|
+
const killProcess = (pid) => {
|
|
3880
|
+
if (isPosix) {
|
|
3881
|
+
try {
|
|
3882
|
+
process.kill(-pid, "SIGKILL");
|
|
3883
|
+
} catch {
|
|
3884
|
+
treeKill(pid, "SIGKILL");
|
|
3885
|
+
}
|
|
3886
|
+
} else {
|
|
3887
|
+
treeKill(pid, "SIGKILL");
|
|
3888
|
+
}
|
|
3889
|
+
};
|
|
3584
3890
|
return await new Promise((resolve) => {
|
|
3585
3891
|
let stdout = "";
|
|
3586
3892
|
let stderr = "";
|
|
@@ -3604,8 +3910,8 @@ Timeout: ${timeout}ms`;
|
|
|
3604
3910
|
}
|
|
3605
3911
|
isResolved = true;
|
|
3606
3912
|
cleanup();
|
|
3607
|
-
const truncatedStdout = await truncateToolResult(stdout);
|
|
3608
|
-
const truncatedStderr = await truncateToolResult(stderr);
|
|
3913
|
+
const truncatedStdout = await truncateToolResult(stdout, 1e3, 50, 1e4);
|
|
3914
|
+
const truncatedStderr = await truncateToolResult(stderr, 1e3, 50, 1e4);
|
|
3609
3915
|
resolve({ stdout: truncatedStdout, stderr: truncatedStderr, exitCode });
|
|
3610
3916
|
};
|
|
3611
3917
|
abortListener = () => {
|
|
@@ -3613,7 +3919,7 @@ Timeout: ${timeout}ms`;
|
|
|
3613
3919
|
return;
|
|
3614
3920
|
}
|
|
3615
3921
|
if (childProcess?.pid) {
|
|
3616
|
-
|
|
3922
|
+
killProcess(childProcess.pid);
|
|
3617
3923
|
}
|
|
3618
3924
|
stderr = "Operation was cancelled by user.";
|
|
3619
3925
|
exitCode = 130;
|
|
@@ -3627,6 +3933,7 @@ Timeout: ${timeout}ms`;
|
|
|
3627
3933
|
env: { ...process.env, TERM: "dumb", DEBIAN_FRONTEND: "noninteractive", PATH: getShellPath() },
|
|
3628
3934
|
stdio: ["ignore", "pipe", "pipe"],
|
|
3629
3935
|
// Explicitly pipe stdout and stderr to capture output from piped commands
|
|
3936
|
+
detached: isPosix,
|
|
3630
3937
|
signal: abortSignal
|
|
3631
3938
|
});
|
|
3632
3939
|
timeoutHandle = setTimeout(() => {
|
|
@@ -3634,7 +3941,7 @@ Timeout: ${timeout}ms`;
|
|
|
3634
3941
|
return;
|
|
3635
3942
|
}
|
|
3636
3943
|
if (childProcess?.pid) {
|
|
3637
|
-
|
|
3944
|
+
killProcess(childProcess.pid);
|
|
3638
3945
|
}
|
|
3639
3946
|
stderr = `Error: Command timed out after ${timeout}ms. Consider increasing the timeout parameter.`;
|
|
3640
3947
|
exitCode = 124;
|
|
@@ -3745,7 +4052,8 @@ Format: ${format}`;
|
|
|
3745
4052
|
return `Error: Invalid URL provided: ${url}. Please provide a valid URL.`;
|
|
3746
4053
|
}
|
|
3747
4054
|
try {
|
|
3748
|
-
|
|
4055
|
+
const content = await scrapeWeb(url, timeout, abortSignal, format);
|
|
4056
|
+
return await truncateToolResult(content, 1e3, 50, 1e4);
|
|
3749
4057
|
} catch (error) {
|
|
3750
4058
|
if (isAbortError(error)) {
|
|
3751
4059
|
return "Operation was cancelled by user.";
|
|
@@ -4014,6 +4322,11 @@ const createTasksToolset = (settings, task, profile, promptContext) => {
|
|
|
4014
4322
|
if (state) {
|
|
4015
4323
|
allTasks = allTasks.filter((t) => t.state === state);
|
|
4016
4324
|
}
|
|
4325
|
+
allTasks.sort((a, b) => {
|
|
4326
|
+
const dateA = a.updatedAt ? new Date(a.updatedAt).getTime() : 0;
|
|
4327
|
+
const dateB = b.updatedAt ? new Date(b.updatedAt).getTime() : 0;
|
|
4328
|
+
return dateB - dateA;
|
|
4329
|
+
});
|
|
4017
4330
|
const startIndex = Math.max(0, offset);
|
|
4018
4331
|
const endIndex = startIndex + Math.max(0, limit || allTasks.length);
|
|
4019
4332
|
const paginatedTasks = allTasks.slice(startIndex, endIndex);
|
|
@@ -4130,34 +4443,35 @@ const createTasksToolset = (settings, task, profile, promptContext) => {
|
|
|
4130
4443
|
}
|
|
4131
4444
|
}
|
|
4132
4445
|
});
|
|
4133
|
-
const isSubtask = task.task.parentId !== null;
|
|
4134
4446
|
const autoGenerateTaskName = settings.taskSettings.autoGenerateTaskName;
|
|
4135
4447
|
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
|
-
});
|
|
4448
|
+
const availableAgents = task.getProject().getAgentProfiles().map((p) => {
|
|
4449
|
+
const slug = deriveDirName(p.name, /* @__PURE__ */ new Set());
|
|
4450
|
+
const shortId = isUuid(p.id) ? p.id.substring(0, 8) : p.id;
|
|
4451
|
+
return `${slug}(${shortId})`;
|
|
4452
|
+
}).join(",");
|
|
4155
4453
|
const createTaskTool = ai.tool({
|
|
4156
4454
|
description: TASKS_TOOL_DESCRIPTIONS[TASKS_TOOL_CREATE_TASK],
|
|
4157
|
-
inputSchema:
|
|
4455
|
+
inputSchema: zod.z.object({
|
|
4456
|
+
prompt: zod.z.string().describe("The initial prompt for the new task"),
|
|
4457
|
+
name: nameProperty,
|
|
4458
|
+
agentProfileId: zod.z.string().optional().describe(`Optional agent profile ID or name. Available agents: ${availableAgents}. Use only when explicitly requested by the user.`),
|
|
4459
|
+
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."),
|
|
4460
|
+
mode: zod.z.string().optional().default("agent").describe("Optional mode to use for the task. Use only when explicitly requested by the user."),
|
|
4461
|
+
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."),
|
|
4462
|
+
parentTaskId: zod.z.string().nullable().optional().describe(
|
|
4463
|
+
"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."
|
|
4464
|
+
),
|
|
4465
|
+
autoApprove: zod.z.boolean().optional().default(false).describe(
|
|
4466
|
+
"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."
|
|
4467
|
+
),
|
|
4468
|
+
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."),
|
|
4469
|
+
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."),
|
|
4470
|
+
executeInBackground: zod.z.boolean().optional().default(false).describe("If true, the task will be created and executed in the background.")
|
|
4471
|
+
}),
|
|
4158
4472
|
execute: async (input, { toolCallId }) => {
|
|
4159
4473
|
const { prompt, name, agentProfileId, modelId, mode = "agent", asSubtask = false, autoApprove, worktree, executeAndWait, executeInBackground } = input;
|
|
4160
|
-
const parentTaskId = asSubtask ? task.taskId : "parentTaskId" in input ? input.parentTaskId : void 0;
|
|
4474
|
+
const parentTaskId = asSubtask ? task.task.parentId || task.taskId : "parentTaskId" in input ? input.parentTaskId : void 0;
|
|
4161
4475
|
task.addToolMessage(toolCallId, TASKS_TOOL_GROUP_NAME, TASKS_TOOL_CREATE_TASK, input, void 0, void 0, promptContext);
|
|
4162
4476
|
const toolName = `${TASKS_TOOL_GROUP_NAME}${TOOL_GROUP_NAME_SEPARATOR}${TASKS_TOOL_CREATE_TASK}`;
|
|
4163
4477
|
const questionKey = toolName;
|
|
@@ -4179,13 +4493,22 @@ Parent Task ID: ${parentTaskId || "none (top-level task)"}` : ""}`;
|
|
|
4179
4493
|
});
|
|
4180
4494
|
const updates = {};
|
|
4181
4495
|
if (agentProfileId) {
|
|
4182
|
-
|
|
4496
|
+
const resolvedProfile = task.getProject().resolveAgentProfile(agentProfileId);
|
|
4497
|
+
if (resolvedProfile) {
|
|
4498
|
+
updates.agentProfileId = resolvedProfile.id;
|
|
4499
|
+
} else {
|
|
4500
|
+
const availableProfiles = task.getProject().getAgentProfiles().map((p) => `"${p.name}"`).join(", ");
|
|
4501
|
+
return `Agent profile '${agentProfileId}' not found. Available profiles: ${availableProfiles}`;
|
|
4502
|
+
}
|
|
4183
4503
|
}
|
|
4184
4504
|
if (modelId) {
|
|
4185
4505
|
const [provider, ...modelParts] = modelId.split("/");
|
|
4186
4506
|
updates.provider = provider;
|
|
4187
4507
|
updates.model = modelParts.join("/");
|
|
4188
4508
|
updates.mainModel = modelId;
|
|
4509
|
+
} else {
|
|
4510
|
+
updates.provider = void 0;
|
|
4511
|
+
updates.model = void 0;
|
|
4189
4512
|
}
|
|
4190
4513
|
const taskInstance = task.getProject().getTask(newTask.id);
|
|
4191
4514
|
if (!taskInstance) {
|
|
@@ -5774,6 +6097,7 @@ ${reminder}
|
|
|
5774
6097
|
"onImportantReminders",
|
|
5775
6098
|
{
|
|
5776
6099
|
profile,
|
|
6100
|
+
agentProfile: profile,
|
|
5777
6101
|
remindersContent
|
|
5778
6102
|
},
|
|
5779
6103
|
task.project,
|
|
@@ -5791,9 +6115,12 @@ ${reminder}
|
|
|
5791
6115
|
${remindersContent}
|
|
5792
6116
|
</ThisIsImportant>`;
|
|
5793
6117
|
}
|
|
5794
|
-
const updatedFirstUserMessage = {
|
|
6118
|
+
const updatedFirstUserMessage = typeof userRequestMessage.content === "string" ? {
|
|
5795
6119
|
...userRequestMessage,
|
|
5796
6120
|
content: `${userRequestMessage.content}${remindersContent}`
|
|
6121
|
+
} : {
|
|
6122
|
+
...userRequestMessage,
|
|
6123
|
+
content: [...userRequestMessage.content, { type: "text", text: remindersContent }]
|
|
5797
6124
|
};
|
|
5798
6125
|
const newMessages = [...messages];
|
|
5799
6126
|
newMessages[userRequestMessageIndex] = updatedFirstUserMessage;
|
|
@@ -6237,9 +6564,9 @@ ${fileList}`
|
|
|
6237
6564
|
const extensionTools = this.extensionManager.createExtensionToolset(task, mode, profile, toolSet, abortSignal);
|
|
6238
6565
|
Object.assign(toolSet, extensionTools);
|
|
6239
6566
|
}
|
|
6240
|
-
return this.wrapToolsWithHooks(task, toolSet, abortSignal, promptContext);
|
|
6567
|
+
return this.wrapToolsWithHooks(task, profile, toolSet, abortSignal, promptContext);
|
|
6241
6568
|
}
|
|
6242
|
-
wrapToolsWithHooks(task, toolSet, abortSignal, promptContext) {
|
|
6569
|
+
wrapToolsWithHooks(task, profile, toolSet, abortSignal, promptContext) {
|
|
6243
6570
|
const wrappedToolSet = {};
|
|
6244
6571
|
for (const [toolName, toolDef] of Object.entries(toolSet)) {
|
|
6245
6572
|
wrappedToolSet[toolName] = {
|
|
@@ -6250,7 +6577,7 @@ ${fileList}`
|
|
|
6250
6577
|
task.addToolMessage(options.toolCallId, serverName, messageToolName, effectiveInput, void 0, void 0, promptContext);
|
|
6251
6578
|
const toolCalledExtensionResult = await this.extensionManager.dispatchEvent(
|
|
6252
6579
|
"onToolCalled",
|
|
6253
|
-
{ toolName, input: effectiveInput, abortSignal: options.abortSignal || abortSignal },
|
|
6580
|
+
{ toolName, agentProfile: profile, input: effectiveInput, abortSignal: options.abortSignal || abortSignal },
|
|
6254
6581
|
task.project,
|
|
6255
6582
|
task
|
|
6256
6583
|
);
|
|
@@ -6267,7 +6594,7 @@ ${fileList}`
|
|
|
6267
6594
|
}
|
|
6268
6595
|
const toolFinishedExtensionResult = await this.extensionManager.dispatchEvent(
|
|
6269
6596
|
"onToolFinished",
|
|
6270
|
-
{ toolName, input: effectiveInput, output: result },
|
|
6597
|
+
{ toolName, agentProfile: profile, input: effectiveInput, output: result },
|
|
6271
6598
|
task.project,
|
|
6272
6599
|
task
|
|
6273
6600
|
);
|
|
@@ -6423,7 +6750,7 @@ ${fileList}`
|
|
|
6423
6750
|
}
|
|
6424
6751
|
return inputSchema;
|
|
6425
6752
|
}
|
|
6426
|
-
async runAgent(task, profile, prompt, mode = "agent", promptContext, initialContextMessages, initialContextFiles, systemPrompt, includeInContext = true, abortSignal) {
|
|
6753
|
+
async runAgent(task, profile, prompt, mode = "agent", promptContext, initialContextMessages, initialContextFiles, systemPrompt, includeInContext = true, abortSignal, images) {
|
|
6427
6754
|
let contextMessages = initialContextMessages ?? await task.getContextMessages();
|
|
6428
6755
|
let contextFiles = initialContextFiles ?? await task.getContextFiles();
|
|
6429
6756
|
if (!systemPrompt) {
|
|
@@ -6432,9 +6759,32 @@ ${fileList}`
|
|
|
6432
6759
|
const userRequestMessage = prompt ? {
|
|
6433
6760
|
id: promptContext?.id || uuid.v4(),
|
|
6434
6761
|
role: "user",
|
|
6435
|
-
content:
|
|
6762
|
+
content: images && images.length > 0 ? [
|
|
6763
|
+
{ type: "text", text: prompt },
|
|
6764
|
+
...images.map((dataUrl) => {
|
|
6765
|
+
const match = dataUrl.match(/^data:(image\/[^;]+);base64,(.+)$/);
|
|
6766
|
+
return {
|
|
6767
|
+
type: "image",
|
|
6768
|
+
image: match ? match[2] : dataUrl,
|
|
6769
|
+
mediaType: match ? match[1] : void 0
|
|
6770
|
+
};
|
|
6771
|
+
})
|
|
6772
|
+
] : prompt,
|
|
6436
6773
|
promptContext
|
|
6437
6774
|
} : null;
|
|
6775
|
+
if (userRequestMessage) {
|
|
6776
|
+
logger.info("User request message created:", {
|
|
6777
|
+
id: userRequestMessage.id,
|
|
6778
|
+
contentType: typeof userRequestMessage.content === "string" ? "string" : "parts",
|
|
6779
|
+
...typeof userRequestMessage.content === "string" ? { contentPreview: userRequestMessage.content.substring(0, 200) } : {
|
|
6780
|
+
parts: userRequestMessage.content.map((part) => ({
|
|
6781
|
+
type: part.type,
|
|
6782
|
+
...part.type === "text" ? { textPreview: part.text?.substring(0, 100) } : {},
|
|
6783
|
+
...part.type === "image" ? { mediaType: part.mediaType, imagePreview: typeof part.image === "string" ? part.image.substring(0, 80) : typeof part.image } : {}
|
|
6784
|
+
}))
|
|
6785
|
+
}
|
|
6786
|
+
});
|
|
6787
|
+
}
|
|
6438
6788
|
const settings = this.store.getSettings();
|
|
6439
6789
|
const projectProfiles = this.agentProfileManager.getProjectProfiles(task.project);
|
|
6440
6790
|
let resultMessages = userRequestMessage ? [userRequestMessage] : [];
|
|
@@ -6653,7 +7003,7 @@ ${fileList}`
|
|
|
6653
7003
|
return null;
|
|
6654
7004
|
}
|
|
6655
7005
|
};
|
|
6656
|
-
const modelSettings = this.modelManager.getModelSettings(provider.
|
|
7006
|
+
const modelSettings = this.modelManager.getModelSettings(provider.id, modelName);
|
|
6657
7007
|
const effectiveTemperature = profile.temperature ?? modelSettings?.temperature;
|
|
6658
7008
|
const effectiveMaxOutputTokens = profile.maxTokens ?? modelSettings?.maxOutputTokens;
|
|
6659
7009
|
logger.debug("Parameters:", {
|
|
@@ -6687,6 +7037,31 @@ ${fileList}`
|
|
|
6687
7037
|
return extensionResult2.optimizedMessages ?? optimized;
|
|
6688
7038
|
};
|
|
6689
7039
|
const optimizedMessages = await getOptimizedMessages();
|
|
7040
|
+
logger.info("Optimized messages for LLM:", {
|
|
7041
|
+
count: optimizedMessages.length,
|
|
7042
|
+
lastUserMessage: (() => {
|
|
7043
|
+
for (let i = optimizedMessages.length - 1; i >= 0; i--) {
|
|
7044
|
+
if (optimizedMessages[i].role === "user") {
|
|
7045
|
+
const msg = optimizedMessages[i];
|
|
7046
|
+
return {
|
|
7047
|
+
index: i,
|
|
7048
|
+
contentType: typeof msg.content,
|
|
7049
|
+
contentIsArray: Array.isArray(msg.content),
|
|
7050
|
+
contentPreview: typeof msg.content === "string" ? msg.content.substring(0, 200) : Array.isArray(msg.content) ? msg.content.map((p) => ({
|
|
7051
|
+
type: p.type,
|
|
7052
|
+
...p.type === "text" ? { textPreview: p.text?.substring(0, 100) } : {},
|
|
7053
|
+
...p.type === "image" ? {
|
|
7054
|
+
mediaType: p.mediaType,
|
|
7055
|
+
imageType: typeof p.image,
|
|
7056
|
+
imagePreview: typeof p.image === "string" ? p.image.substring(0, 80) : void 0
|
|
7057
|
+
} : {}
|
|
7058
|
+
})) : String(msg.content).substring(0, 200)
|
|
7059
|
+
};
|
|
7060
|
+
}
|
|
7061
|
+
}
|
|
7062
|
+
return null;
|
|
7063
|
+
})()
|
|
7064
|
+
});
|
|
6690
7065
|
return {
|
|
6691
7066
|
providerOptions,
|
|
6692
7067
|
model: ai.wrapLanguageModel({
|
|
@@ -6713,7 +7088,7 @@ ${fileList}`
|
|
|
6713
7088
|
while (true) {
|
|
6714
7089
|
logger.info(`Starting iteration ${iterationCount}`);
|
|
6715
7090
|
iterationCount++;
|
|
6716
|
-
if (iterationCount > profile.maxIterations) {
|
|
7091
|
+
if (profile.maxIterations > 0 && iterationCount > profile.maxIterations) {
|
|
6717
7092
|
logger.warn(`Max iterations (${profile.maxIterations}) reached. Stopping agent.`);
|
|
6718
7093
|
task.addLogMessage(
|
|
6719
7094
|
"warning",
|
|
@@ -6726,7 +7101,7 @@ ${fileList}`
|
|
|
6726
7101
|
let iterationError = null;
|
|
6727
7102
|
let hasReasoning = false;
|
|
6728
7103
|
let finishReason = null;
|
|
6729
|
-
let
|
|
7104
|
+
let currentStepMessages = [];
|
|
6730
7105
|
let responseMessageIndex = 0;
|
|
6731
7106
|
const onStepFinish = async (stepResult) => {
|
|
6732
7107
|
finishReason = stepResult.finishReason;
|
|
@@ -6738,7 +7113,7 @@ ${fileList}`
|
|
|
6738
7113
|
logger.info("Prompt aborted by user");
|
|
6739
7114
|
return;
|
|
6740
7115
|
}
|
|
6741
|
-
|
|
7116
|
+
currentStepMessages = await this.processStep(currentResponseId, stepResult, task, provider, modelName, promptContext, abortSignal);
|
|
6742
7117
|
const extensionResult2 = await this.extensionManager.dispatchEvent(
|
|
6743
7118
|
"onAgentStepFinished",
|
|
6744
7119
|
{
|
|
@@ -6747,18 +7122,18 @@ ${fileList}`
|
|
|
6747
7122
|
currentResponseId,
|
|
6748
7123
|
stepResult,
|
|
6749
7124
|
finishReason,
|
|
6750
|
-
responseMessages
|
|
7125
|
+
responseMessages: currentStepMessages
|
|
6751
7126
|
},
|
|
6752
7127
|
task.project,
|
|
6753
7128
|
task
|
|
6754
7129
|
);
|
|
6755
|
-
|
|
7130
|
+
currentStepMessages = extensionResult2.responseMessages;
|
|
6756
7131
|
finishReason = extensionResult2.finishReason;
|
|
6757
7132
|
currentResponseId = uuid.v4();
|
|
6758
7133
|
responseMessageIndex = 0;
|
|
6759
7134
|
hasReasoning = false;
|
|
6760
7135
|
streamingMessageIds.clear();
|
|
6761
|
-
if (
|
|
7136
|
+
if (currentStepMessages.length > 0) {
|
|
6762
7137
|
retryCount = 0;
|
|
6763
7138
|
}
|
|
6764
7139
|
};
|
|
@@ -6831,25 +7206,46 @@ ${fileList}`
|
|
|
6831
7206
|
onStepFinish,
|
|
6832
7207
|
experimental_repairToolCall: repairToolCall
|
|
6833
7208
|
});
|
|
6834
|
-
|
|
6835
|
-
|
|
6836
|
-
|
|
6837
|
-
|
|
6838
|
-
if (
|
|
7209
|
+
try {
|
|
7210
|
+
for await (const chunk of result.fullStream) {
|
|
7211
|
+
logger.debug("Chunk:", { chunk: chunk.type, responseMessageIndex });
|
|
7212
|
+
const responseMessageId = responseMessageIndex > 0 ? `${currentResponseId}-${responseMessageIndex}` : currentResponseId;
|
|
7213
|
+
if (chunk.type === "text-start") {
|
|
7214
|
+
if (hasReasoning) {
|
|
7215
|
+
streamingMessageIds.add(responseMessageId);
|
|
7216
|
+
await task.processResponseMessage({
|
|
7217
|
+
id: responseMessageId,
|
|
7218
|
+
action: "response",
|
|
7219
|
+
content: ANSWER_RESPONSE_START_TAG,
|
|
7220
|
+
finished: false,
|
|
7221
|
+
promptContext
|
|
7222
|
+
});
|
|
7223
|
+
hasReasoning = false;
|
|
7224
|
+
}
|
|
7225
|
+
} else if (chunk.type === "text-end") {
|
|
7226
|
+
responseMessageIndex++;
|
|
7227
|
+
} else if (chunk.type === "text-delta") {
|
|
7228
|
+
if (chunk.text.trim()) {
|
|
7229
|
+
streamingMessageIds.add(responseMessageId);
|
|
7230
|
+
await task.processResponseMessage({
|
|
7231
|
+
id: responseMessageId,
|
|
7232
|
+
action: "response",
|
|
7233
|
+
content: chunk.text,
|
|
7234
|
+
finished: false,
|
|
7235
|
+
promptContext
|
|
7236
|
+
});
|
|
7237
|
+
}
|
|
7238
|
+
} else if (chunk.type === "reasoning-start") {
|
|
6839
7239
|
streamingMessageIds.add(responseMessageId);
|
|
6840
7240
|
await task.processResponseMessage({
|
|
6841
7241
|
id: responseMessageId,
|
|
6842
7242
|
action: "response",
|
|
6843
|
-
content:
|
|
7243
|
+
content: THINKING_RESPONSE_STAR_TAG,
|
|
6844
7244
|
finished: false,
|
|
6845
7245
|
promptContext
|
|
6846
7246
|
});
|
|
6847
|
-
hasReasoning =
|
|
6848
|
-
}
|
|
6849
|
-
} else if (chunk.type === "text-end") {
|
|
6850
|
-
responseMessageIndex++;
|
|
6851
|
-
} else if (chunk.type === "text-delta") {
|
|
6852
|
-
if (chunk.text.trim()) {
|
|
7247
|
+
hasReasoning = true;
|
|
7248
|
+
} else if (chunk.type === "reasoning-delta") {
|
|
6853
7249
|
streamingMessageIds.add(responseMessageId);
|
|
6854
7250
|
await task.processResponseMessage({
|
|
6855
7251
|
id: responseMessageId,
|
|
@@ -6858,51 +7254,53 @@ ${fileList}`
|
|
|
6858
7254
|
finished: false,
|
|
6859
7255
|
promptContext
|
|
6860
7256
|
});
|
|
7257
|
+
} else if (chunk.type === "tool-input-start") {
|
|
7258
|
+
task.addLogMessage("loading", "Preparing tool...", false, promptContext);
|
|
7259
|
+
streamingMessageIds.add(chunk.id);
|
|
7260
|
+
} else if (chunk.type === "tool-call") {
|
|
7261
|
+
task.addLogMessage("loading", "Executing tool...", false, promptContext);
|
|
7262
|
+
streamingMessageIds.add(chunk.toolCallId);
|
|
7263
|
+
} else if (chunk.type === "tool-result") {
|
|
7264
|
+
const [serverName, toolName] = extractServerNameToolName(chunk.toolName);
|
|
7265
|
+
const toolPromptContext = extractPromptContextFromToolResult(chunk.output) ?? promptContext;
|
|
7266
|
+
streamingMessageIds.add(chunk.toolCallId);
|
|
7267
|
+
task.addToolMessage(chunk.toolCallId, serverName, toolName, chunk.input, JSON.stringify(chunk.output), void 0, toolPromptContext);
|
|
7268
|
+
task.addLogMessage("loading", void 0, false, promptContext);
|
|
6861
7269
|
}
|
|
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);
|
|
7270
|
+
}
|
|
7271
|
+
} catch (streamError) {
|
|
7272
|
+
if (effectiveAbortSignal?.aborted) {
|
|
7273
|
+
throw streamError;
|
|
7274
|
+
}
|
|
7275
|
+
if (isNetworkError(streamError) && retryCount < MAX_RETRIES) {
|
|
7276
|
+
logger.warn(`Network error during streaming, retrying (${retryCount + 1}/${MAX_RETRIES})...`, { error: streamError });
|
|
7277
|
+
iterationError = streamError;
|
|
7278
|
+
this.removeUnfinishedStreamingMessages(task, streamingMessageIds);
|
|
7279
|
+
task.addLogMessage("loading", "Network error occured. Retrying...");
|
|
7280
|
+
} else if (isNetworkError(streamError)) {
|
|
7281
|
+
const error = streamError;
|
|
7282
|
+
const underlyingMessage = error.cause instanceof Error ? error.cause.message : error.message;
|
|
7283
|
+
throw new Error(`Network error after ${MAX_RETRIES} retries (check your connection): ${underlyingMessage}`, { cause: streamError });
|
|
7284
|
+
} else {
|
|
7285
|
+
throw streamError;
|
|
6893
7286
|
}
|
|
6894
7287
|
}
|
|
6895
7288
|
}
|
|
6896
7289
|
if (iterationError) {
|
|
6897
|
-
logger.error("Error during
|
|
6898
|
-
if (iterationError
|
|
7290
|
+
logger.error("Error during iteration:", iterationError);
|
|
7291
|
+
if (isNetworkError(iterationError) && retryCount < MAX_RETRIES) {
|
|
7292
|
+
logger.info(`Network error, retrying (${retryCount + 1}/${MAX_RETRIES})...`);
|
|
7293
|
+
this.removeUnfinishedStreamingMessages(task, streamingMessageIds);
|
|
7294
|
+
retryCount++;
|
|
7295
|
+
continue;
|
|
7296
|
+
} else if (iterationError instanceof ai.APICallError && iterationError.isRetryable && this.modelManager.isRetryable(resolvedProvider, modelName, iterationError)) {
|
|
6899
7297
|
continue;
|
|
6900
7298
|
} else {
|
|
6901
7299
|
break;
|
|
6902
7300
|
}
|
|
6903
7301
|
}
|
|
6904
|
-
const newMessages = this.filterResultMessages(
|
|
6905
|
-
messages.push(...
|
|
7302
|
+
const newMessages = this.filterResultMessages(currentStepMessages);
|
|
7303
|
+
messages.push(...currentStepMessages);
|
|
6906
7304
|
resultMessages.push(...newMessages);
|
|
6907
7305
|
if (includeInContext) {
|
|
6908
7306
|
try {
|
|
@@ -6928,7 +7326,7 @@ ${fileList}`
|
|
|
6928
7326
|
retryCount++;
|
|
6929
7327
|
continue;
|
|
6930
7328
|
}
|
|
6931
|
-
const lastMessage =
|
|
7329
|
+
const lastMessage = currentStepMessages[currentStepMessages.length - 1];
|
|
6932
7330
|
if (finishReason === "stop" && lastMessage?.role === "tool") {
|
|
6933
7331
|
logger.debug('Finish reason is "stop" but last message is a tool call. Retrying...');
|
|
6934
7332
|
retryCount++;
|
|
@@ -6990,6 +7388,9 @@ ${fileList}`
|
|
|
6990
7388
|
} else {
|
|
6991
7389
|
task.addLogMessage("error", `${error instanceof Error ? error.message : String(error)}`, false, promptContext);
|
|
6992
7390
|
}
|
|
7391
|
+
await task.updateTask({
|
|
7392
|
+
state: DefaultTaskState.Interrupted
|
|
7393
|
+
});
|
|
6993
7394
|
} finally {
|
|
6994
7395
|
if (controllerId) {
|
|
6995
7396
|
this.abortControllers.delete(controllerId);
|
|
@@ -7286,11 +7687,9 @@ ${fileList}`
|
|
|
7286
7687
|
optimizedMessages.unshift({ role: "system", content: systemPrompt });
|
|
7287
7688
|
const chatMessages = optimizedMessages.map((msg) => ({
|
|
7288
7689
|
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
|
|
7690
|
+
content: msg.content
|
|
7292
7691
|
}));
|
|
7293
|
-
return
|
|
7692
|
+
return estimateMessageTokens(chatMessages);
|
|
7294
7693
|
} catch (error) {
|
|
7295
7694
|
logger.error(`Error counting tokens: ${error}`);
|
|
7296
7695
|
return 0;
|
|
@@ -7398,18 +7797,64 @@ ${fileList}`
|
|
|
7398
7797
|
return messages;
|
|
7399
7798
|
}
|
|
7400
7799
|
async compactMessagesIfNeeded(task, profile, provider, model, userRequestMessage, contextMessages, contextFiles, messages, resultMessages, promptContext, abortSignal) {
|
|
7401
|
-
const
|
|
7402
|
-
const
|
|
7800
|
+
const taskSettings = this.store.getSettings().taskSettings;
|
|
7801
|
+
const thresholdConfig = {
|
|
7802
|
+
percentage: profile.autoCompactThresholdPercentage ?? taskSettings.contextCompactingThreshold.percentage,
|
|
7803
|
+
tokens: profile.autoCompactThresholdTokens ?? taskSettings.contextCompactingThreshold.tokens
|
|
7804
|
+
};
|
|
7805
|
+
const taskTokensOverride = task.task.contextCompactingThresholdTokens;
|
|
7806
|
+
let contextCompactionType = profile.autoCompactionType ?? taskSettings.contextCompactionType ?? ContextCompactionType.Compact;
|
|
7807
|
+
if (profile.isSubagent && contextCompactionType === ContextCompactionType.Handoff) {
|
|
7808
|
+
contextCompactionType = ContextCompactionType.Compact;
|
|
7809
|
+
}
|
|
7810
|
+
logger.debug("Compaction threshold", {
|
|
7811
|
+
thresholdConfig,
|
|
7812
|
+
profile: {
|
|
7813
|
+
autoCompactThresholdPercentage: profile.autoCompactThresholdPercentage,
|
|
7814
|
+
autoCompactThresholdTokens: profile.autoCompactThresholdTokens
|
|
7815
|
+
},
|
|
7816
|
+
taskSettings: {
|
|
7817
|
+
contextCompactingThreshold: taskSettings.contextCompactingThreshold
|
|
7818
|
+
},
|
|
7819
|
+
taskTokensOverride,
|
|
7820
|
+
contextCompactionType
|
|
7821
|
+
});
|
|
7403
7822
|
const lastAssistantMessage = [...resultMessages].reverse().find((m) => m.role === "assistant");
|
|
7404
7823
|
const usageReport = lastAssistantMessage?.usageReport;
|
|
7405
|
-
const maxTokens = this.modelManager.getModelSettings(provider.
|
|
7406
|
-
if (
|
|
7824
|
+
const maxTokens = this.modelManager.getModelSettings(provider.id, model)?.maxInputTokens;
|
|
7825
|
+
if (!usageReport || !maxTokens) {
|
|
7826
|
+
logger.debug("No usageReport or maxTokens", {
|
|
7827
|
+
usageReport,
|
|
7828
|
+
maxTokens
|
|
7829
|
+
});
|
|
7407
7830
|
return true;
|
|
7408
7831
|
}
|
|
7409
7832
|
const totalTokens = usageReport.sentTokens + usageReport.receivedTokens + (usageReport.cacheReadTokens ?? 0);
|
|
7410
|
-
|
|
7833
|
+
let effectiveThreshold;
|
|
7834
|
+
let thresholdDescription;
|
|
7835
|
+
if (taskTokensOverride !== void 0) {
|
|
7836
|
+
if (taskTokensOverride === 0) {
|
|
7837
|
+
return true;
|
|
7838
|
+
}
|
|
7839
|
+
effectiveThreshold = taskTokensOverride;
|
|
7840
|
+
thresholdDescription = `task override: ${taskTokensOverride}`;
|
|
7841
|
+
} else {
|
|
7842
|
+
if (thresholdConfig.percentage === 0) {
|
|
7843
|
+
return true;
|
|
7844
|
+
}
|
|
7845
|
+
const percentageThreshold = maxTokens * thresholdConfig.percentage / 100;
|
|
7846
|
+
const tokenThreshold = thresholdConfig.tokens;
|
|
7847
|
+
effectiveThreshold = Math.min(percentageThreshold, tokenThreshold);
|
|
7848
|
+
thresholdDescription = `percentage: ${percentageThreshold}, tokens: ${tokenThreshold}`;
|
|
7849
|
+
}
|
|
7850
|
+
logger.debug("Checking total tokens vs effective threshold", {
|
|
7851
|
+
totalTokens,
|
|
7852
|
+
effectiveThreshold,
|
|
7853
|
+
thresholdDescription
|
|
7854
|
+
});
|
|
7855
|
+
if (totalTokens > effectiveThreshold) {
|
|
7411
7856
|
logger.info(
|
|
7412
|
-
`Token usage ${totalTokens} exceeds threshold of ${
|
|
7857
|
+
`Token usage ${totalTokens} exceeds effective threshold of ${effectiveThreshold} (${thresholdDescription}). Compacting conversation with type: ${contextCompactionType}.`
|
|
7413
7858
|
);
|
|
7414
7859
|
if (contextCompactionType === ContextCompactionType.Compact) {
|
|
7415
7860
|
await task.compactConversation(
|
|
@@ -7425,15 +7870,24 @@ ${fileList}`
|
|
|
7425
7870
|
messages.length = 0;
|
|
7426
7871
|
resultMessages.length = 0;
|
|
7427
7872
|
messages.push(...await this.prepareMessages(task, profile, await task.getContextMessages(), contextFiles));
|
|
7873
|
+
const continuationText = `Based on your compacted summary of our previous conversation, please continue our work with my request:
|
|
7874
|
+
|
|
7875
|
+
${extractTextContent(userRequestMessage.content)}`;
|
|
7876
|
+
const originalImageParts = Array.isArray(userRequestMessage.content) ? userRequestMessage.content.filter((part) => part.type === "image") : [];
|
|
7428
7877
|
resultMessages.push({
|
|
7429
7878
|
id: uuid.v4(),
|
|
7430
7879
|
role: "user",
|
|
7431
|
-
content:
|
|
7432
|
-
|
|
7433
|
-
${userRequestMessage.content}`,
|
|
7880
|
+
content: originalImageParts.length > 0 ? [{ type: "text", text: continuationText }, ...originalImageParts] : continuationText,
|
|
7434
7881
|
promptContext
|
|
7435
7882
|
});
|
|
7436
7883
|
messages.push(...resultMessages);
|
|
7884
|
+
} else if (contextCompactionType === ContextCompactionType.Smart) {
|
|
7885
|
+
const compactedMessages = await task.smartCompactConversation([...contextMessages, ...resultMessages], "Previous conversation has been compacted.");
|
|
7886
|
+
contextMessages.length = 0;
|
|
7887
|
+
messages.length = 0;
|
|
7888
|
+
resultMessages.length = 0;
|
|
7889
|
+
contextMessages.push(...compactedMessages);
|
|
7890
|
+
messages.push(...await this.prepareMessages(task, profile, compactedMessages, contextFiles));
|
|
7437
7891
|
} else if (contextCompactionType === ContextCompactionType.Handoff) {
|
|
7438
7892
|
await task.handoffConversation(
|
|
7439
7893
|
"agent",
|
|
@@ -7874,6 +8328,37 @@ class AgentProfileManager {
|
|
|
7874
8328
|
}
|
|
7875
8329
|
return this.profiles.get(profileId)?.agentProfile;
|
|
7876
8330
|
}
|
|
8331
|
+
resolveAgentProfile(id) {
|
|
8332
|
+
const lowerId = id.toLowerCase();
|
|
8333
|
+
const directMatch = this.getProfile(id);
|
|
8334
|
+
if (directMatch) {
|
|
8335
|
+
return directMatch;
|
|
8336
|
+
}
|
|
8337
|
+
const allProfiles = this.getAllProfiles();
|
|
8338
|
+
for (const profile of allProfiles) {
|
|
8339
|
+
if (profile.name.toLowerCase() === lowerId) {
|
|
8340
|
+
return profile;
|
|
8341
|
+
}
|
|
8342
|
+
}
|
|
8343
|
+
for (const context of this.profiles.values()) {
|
|
8344
|
+
if (context.dirName.toLowerCase() === lowerId) {
|
|
8345
|
+
return context.agentProfile;
|
|
8346
|
+
}
|
|
8347
|
+
}
|
|
8348
|
+
const slugifiedId = deriveDirName(id, /* @__PURE__ */ new Set()).toLowerCase();
|
|
8349
|
+
for (const context of this.profiles.values()) {
|
|
8350
|
+
if (context.dirName.toLowerCase() === slugifiedId) {
|
|
8351
|
+
return context.agentProfile;
|
|
8352
|
+
}
|
|
8353
|
+
}
|
|
8354
|
+
for (const profile of allProfiles) {
|
|
8355
|
+
const derivedName = deriveDirName(profile.name, /* @__PURE__ */ new Set()).toLowerCase();
|
|
8356
|
+
if (derivedName === slugifiedId) {
|
|
8357
|
+
return profile;
|
|
8358
|
+
}
|
|
8359
|
+
}
|
|
8360
|
+
return null;
|
|
8361
|
+
}
|
|
7877
8362
|
getOrderedProfiles(profileContexts) {
|
|
7878
8363
|
return profileContexts.sort((a, b) => {
|
|
7879
8364
|
const aIsProject = !!a.agentProfile.projectDir;
|
|
@@ -8213,7 +8698,8 @@ const RunPromptSchema = zod.z.object({
|
|
|
8213
8698
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
8214
8699
|
taskId: zod.z.string().min(1, "Task ID is required"),
|
|
8215
8700
|
prompt: zod.z.string().min(1, "Prompt is required"),
|
|
8216
|
-
mode: zod.z.string().optional()
|
|
8701
|
+
mode: zod.z.string().optional(),
|
|
8702
|
+
images: zod.z.array(zod.z.string()).optional()
|
|
8217
8703
|
});
|
|
8218
8704
|
const SavePromptSchema = zod.z.object({
|
|
8219
8705
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
@@ -8233,8 +8719,8 @@ class PromptApi extends BaseApi {
|
|
|
8233
8719
|
if (!parsed) {
|
|
8234
8720
|
return;
|
|
8235
8721
|
}
|
|
8236
|
-
const { projectDir, taskId, prompt, mode } = parsed;
|
|
8237
|
-
const responses = await this.eventsHandler.runPrompt(projectDir, taskId, prompt, mode);
|
|
8722
|
+
const { projectDir, taskId, prompt, mode, images } = parsed;
|
|
8723
|
+
const responses = await this.eventsHandler.runPrompt(projectDir, taskId, prompt, mode, images);
|
|
8238
8724
|
res.status(200).json(responses);
|
|
8239
8725
|
})
|
|
8240
8726
|
);
|
|
@@ -8422,6 +8908,10 @@ const ClearContextSchema = zod.z.object({
|
|
|
8422
8908
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
8423
8909
|
taskId: zod.z.string().min(1, "Task id is required")
|
|
8424
8910
|
});
|
|
8911
|
+
const UndoContextChangeSchema = zod.z.object({
|
|
8912
|
+
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
8913
|
+
taskId: zod.z.string().min(1, "Task id is required")
|
|
8914
|
+
});
|
|
8425
8915
|
const AnswerQuestionSchema = zod.z.object({
|
|
8426
8916
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
8427
8917
|
taskId: zod.z.string().min(1, "Task id is required"),
|
|
@@ -8500,7 +8990,8 @@ const RedoUserPromptSchema = zod.z.object({
|
|
|
8500
8990
|
taskId: zod.z.string().min(1, "Task id is required"),
|
|
8501
8991
|
messageId: zod.z.string().min(1, "Message id is required"),
|
|
8502
8992
|
mode: zod.z.string().min(1, "Mode is required"),
|
|
8503
|
-
updatedPrompt: zod.z.string().optional()
|
|
8993
|
+
updatedPrompt: zod.z.string().optional(),
|
|
8994
|
+
updatedImages: zod.z.array(zod.z.string()).optional()
|
|
8504
8995
|
});
|
|
8505
8996
|
const ResumeTaskSchema = zod.z.object({
|
|
8506
8997
|
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
@@ -8607,6 +9098,10 @@ const HandoffConversationSchema = zod.z.object({
|
|
|
8607
9098
|
taskId: zod.z.string().min(1, "Task id is required"),
|
|
8608
9099
|
focus: zod.z.string().optional()
|
|
8609
9100
|
});
|
|
9101
|
+
const SmartCompactConversationSchema = zod.z.object({
|
|
9102
|
+
projectDir: zod.z.string().min(1, "Project directory is required"),
|
|
9103
|
+
taskId: zod.z.string().min(1, "Task id is required")
|
|
9104
|
+
});
|
|
8610
9105
|
const ChangeRequestItemSchema = zod.z.object({
|
|
8611
9106
|
filename: zod.z.string().min(1, "Filename is required"),
|
|
8612
9107
|
lineNumber: zod.z.number().int().min(1, "Line number is required"),
|
|
@@ -8727,8 +9222,8 @@ class ProjectApi extends BaseApi {
|
|
|
8727
9222
|
if (!parsed) {
|
|
8728
9223
|
return;
|
|
8729
9224
|
}
|
|
8730
|
-
const { projectDir, taskId, messageId, mode, updatedPrompt } = parsed;
|
|
8731
|
-
await this.eventsHandler.redoUserPrompt(projectDir, taskId, messageId, mode, updatedPrompt);
|
|
9225
|
+
const { projectDir, taskId, messageId, mode, updatedPrompt, updatedImages } = parsed;
|
|
9226
|
+
await this.eventsHandler.redoUserPrompt(projectDir, taskId, messageId, mode, updatedPrompt, updatedImages);
|
|
8732
9227
|
res.status(200).json({ message: "Redo user prompt initiated" });
|
|
8733
9228
|
})
|
|
8734
9229
|
);
|
|
@@ -9013,6 +9508,30 @@ class ProjectApi extends BaseApi {
|
|
|
9013
9508
|
res.status(200).json({ message: "Conversation compacted" });
|
|
9014
9509
|
})
|
|
9015
9510
|
);
|
|
9511
|
+
router.post(
|
|
9512
|
+
"/project/smart-compact-conversation",
|
|
9513
|
+
this.handleRequest(async (req, res) => {
|
|
9514
|
+
const parsed = this.validateRequest(SmartCompactConversationSchema, req.body, res);
|
|
9515
|
+
if (!parsed) {
|
|
9516
|
+
return;
|
|
9517
|
+
}
|
|
9518
|
+
const { projectDir, taskId } = parsed;
|
|
9519
|
+
await this.eventsHandler.smartCompactConversation(projectDir, taskId);
|
|
9520
|
+
res.status(200).json({ message: "Conversation smart-compacted" });
|
|
9521
|
+
})
|
|
9522
|
+
);
|
|
9523
|
+
router.post(
|
|
9524
|
+
"/project/undo-context-change",
|
|
9525
|
+
this.handleRequest(async (req, res) => {
|
|
9526
|
+
const parsed = this.validateRequest(UndoContextChangeSchema, req.body, res);
|
|
9527
|
+
if (!parsed) {
|
|
9528
|
+
return;
|
|
9529
|
+
}
|
|
9530
|
+
const { projectDir, taskId } = parsed;
|
|
9531
|
+
const undone = await this.eventsHandler.undoContextChange(projectDir, taskId);
|
|
9532
|
+
res.status(200).json({ undone });
|
|
9533
|
+
})
|
|
9534
|
+
);
|
|
9016
9535
|
router.post(
|
|
9017
9536
|
"/project/handoff-conversation",
|
|
9018
9537
|
this.handleRequest(async (req, res) => {
|
|
@@ -10985,13 +11504,15 @@ class CustomCommandManager {
|
|
|
10985
11504
|
const template = parsed.__content?.trim() || "";
|
|
10986
11505
|
const includeContext = typeof parsed.includeContext === "boolean" ? parsed.includeContext : true;
|
|
10987
11506
|
const autoApprove = typeof parsed.autoApprove === "boolean" ? parsed.autoApprove : void 0;
|
|
11507
|
+
const skills = typeof parsed.skills === "string" && parsed.skills.trim() ? parsed.skills.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
|
|
10988
11508
|
commands.set(name, {
|
|
10989
11509
|
name,
|
|
10990
11510
|
description: parsed.description || "Not specified",
|
|
10991
11511
|
arguments: args,
|
|
10992
11512
|
template,
|
|
10993
11513
|
includeContext,
|
|
10994
|
-
autoApprove
|
|
11514
|
+
autoApprove,
|
|
11515
|
+
skills
|
|
10995
11516
|
});
|
|
10996
11517
|
} catch (err) {
|
|
10997
11518
|
logger.error(`Failed to parse command file ${filePath}: ${err}`);
|
|
@@ -11225,10 +11746,20 @@ class ContextManager {
|
|
|
11225
11746
|
}
|
|
11226
11747
|
messages;
|
|
11227
11748
|
files;
|
|
11749
|
+
undoSnapshot = null;
|
|
11228
11750
|
loadPromise = null;
|
|
11229
11751
|
loaded = false;
|
|
11230
11752
|
autosaveEnabled = false;
|
|
11231
11753
|
storagePath;
|
|
11754
|
+
hasUndoSnapshot() {
|
|
11755
|
+
return this.undoSnapshot !== null;
|
|
11756
|
+
}
|
|
11757
|
+
undoContextChange() {
|
|
11758
|
+
const snapshot = this.undoSnapshot;
|
|
11759
|
+
this.undoSnapshot = null;
|
|
11760
|
+
this.task.sendContextInfoUpdated();
|
|
11761
|
+
return snapshot;
|
|
11762
|
+
}
|
|
11232
11763
|
enableAutosave() {
|
|
11233
11764
|
logger.debug("Enabling autosave for task", { taskId: this.taskId });
|
|
11234
11765
|
this.autosaveEnabled = true;
|
|
@@ -11260,6 +11791,10 @@ class ContextManager {
|
|
|
11260
11791
|
}
|
|
11261
11792
|
this.messages.push(message);
|
|
11262
11793
|
logger.debug(`Task ${this.taskId}: Added ${message.role} message. Total messages: ${this.messages.length}`);
|
|
11794
|
+
if (this.undoSnapshot !== null) {
|
|
11795
|
+
this.undoSnapshot = null;
|
|
11796
|
+
this.task.sendContextInfoUpdated();
|
|
11797
|
+
}
|
|
11263
11798
|
this.autosave();
|
|
11264
11799
|
}
|
|
11265
11800
|
async isFileIgnored(contextFile) {
|
|
@@ -11277,7 +11812,14 @@ class ContextManager {
|
|
|
11277
11812
|
});
|
|
11278
11813
|
return [];
|
|
11279
11814
|
}
|
|
11280
|
-
|
|
11815
|
+
const existingContextFile = this.findExistingContextFile(absolutePath);
|
|
11816
|
+
if (existingContextFile) {
|
|
11817
|
+
const readOnly = contextFile.readOnly ?? false;
|
|
11818
|
+
if (existingContextFile.readOnly !== readOnly) {
|
|
11819
|
+
existingContextFile.readOnly = readOnly;
|
|
11820
|
+
this.autosave();
|
|
11821
|
+
return [existingContextFile];
|
|
11822
|
+
}
|
|
11281
11823
|
return [];
|
|
11282
11824
|
}
|
|
11283
11825
|
const isDir = await isDirectory(absolutePath);
|
|
@@ -11336,11 +11878,11 @@ class ContextManager {
|
|
|
11336
11878
|
return this.task.resolveContextFilePath(relativePath);
|
|
11337
11879
|
}
|
|
11338
11880
|
/**
|
|
11339
|
-
*
|
|
11881
|
+
* Finds an already-added context file matching the given resolved absolute path, if any.
|
|
11340
11882
|
* Accounts for files that may have been resolved from either taskDir or projectDir.
|
|
11341
11883
|
*/
|
|
11342
|
-
|
|
11343
|
-
return this.files.
|
|
11884
|
+
findExistingContextFile(absolutePath) {
|
|
11885
|
+
return this.files.find((file) => {
|
|
11344
11886
|
const taskDirResolved = path.resolve(this.task.getTaskDir(), file.path);
|
|
11345
11887
|
const projectDirResolved = path.resolve(this.task.getProjectDir(), file.path);
|
|
11346
11888
|
return taskDirResolved === absolutePath || projectDirResolved === absolutePath;
|
|
@@ -11394,6 +11936,10 @@ class ContextManager {
|
|
|
11394
11936
|
messages: contextMessages.length,
|
|
11395
11937
|
save
|
|
11396
11938
|
});
|
|
11939
|
+
if (this.messages.length > 0 && this.messages !== contextMessages) {
|
|
11940
|
+
this.undoSnapshot = [...this.messages];
|
|
11941
|
+
this.task.sendContextInfoUpdated();
|
|
11942
|
+
}
|
|
11397
11943
|
this.messages = contextMessages;
|
|
11398
11944
|
if (save) {
|
|
11399
11945
|
this.autosave();
|
|
@@ -11423,8 +11969,12 @@ class ContextManager {
|
|
|
11423
11969
|
this.autosave();
|
|
11424
11970
|
}
|
|
11425
11971
|
}
|
|
11426
|
-
clearMessages(save = true) {
|
|
11972
|
+
clearMessages(save = true, createSnapshot = true) {
|
|
11427
11973
|
logger.debug("Clearing task messages", { taskId: this.taskId });
|
|
11974
|
+
if (createSnapshot && this.messages.length > 0) {
|
|
11975
|
+
this.undoSnapshot = [...this.messages];
|
|
11976
|
+
this.task.sendContextInfoUpdated();
|
|
11977
|
+
}
|
|
11428
11978
|
this.messages = [];
|
|
11429
11979
|
if (save) {
|
|
11430
11980
|
this.autosave();
|
|
@@ -11721,6 +12271,34 @@ ${JSON.stringify(part.output.value)}`
|
|
|
11721
12271
|
throw error;
|
|
11722
12272
|
}
|
|
11723
12273
|
}
|
|
12274
|
+
/**
|
|
12275
|
+
* Creates a backup of the current context.json file before a destructive operation
|
|
12276
|
+
* (e.g., smart compaction). Backups are named context.backup.001.json, context.backup.002.json, etc.
|
|
12277
|
+
* For debugging purposes only.
|
|
12278
|
+
*/
|
|
12279
|
+
async backupContext() {
|
|
12280
|
+
try {
|
|
12281
|
+
const dir = path.dirname(this.storagePath);
|
|
12282
|
+
const files = await fs$1.promises.readdir(dir);
|
|
12283
|
+
const backupPattern = /^context\.backup\.(\d+)\.json$/;
|
|
12284
|
+
let maxNumber = 0;
|
|
12285
|
+
for (const file of files) {
|
|
12286
|
+
const match = backupPattern.exec(file);
|
|
12287
|
+
if (match) {
|
|
12288
|
+
const num = parseInt(match[1], 10);
|
|
12289
|
+
if (num > maxNumber) {
|
|
12290
|
+
maxNumber = num;
|
|
12291
|
+
}
|
|
12292
|
+
}
|
|
12293
|
+
}
|
|
12294
|
+
const nextNumber = String(maxNumber + 1).padStart(3, "0");
|
|
12295
|
+
const backupPath = path.join(dir, `context.backup.${nextNumber}.json`);
|
|
12296
|
+
await fs$1.promises.copyFile(this.storagePath, backupPath);
|
|
12297
|
+
logger.debug(`Task context backed up to ${backupPath}`, { taskId: this.taskId });
|
|
12298
|
+
} catch (error) {
|
|
12299
|
+
logger.error("Failed to backup task context:", { error, taskId: this.taskId });
|
|
12300
|
+
}
|
|
12301
|
+
}
|
|
11724
12302
|
async cleanupContext() {
|
|
11725
12303
|
const existingFiles = [];
|
|
11726
12304
|
for (const file of this.files) {
|
|
@@ -11794,8 +12372,8 @@ ${JSON.stringify(part.output.value)}`
|
|
|
11794
12372
|
this.loaded = true;
|
|
11795
12373
|
await this.cleanupContext();
|
|
11796
12374
|
}
|
|
11797
|
-
async loadMessages(messages) {
|
|
11798
|
-
await this.task.clearContext(false, false);
|
|
12375
|
+
async loadMessages(messages, updateTaskState = true) {
|
|
12376
|
+
await this.task.clearContext(false, false, updateTaskState, false);
|
|
11799
12377
|
this.messages = messages;
|
|
11800
12378
|
const messagesData = this.getContextMessagesData(messages);
|
|
11801
12379
|
for (const messageData of messagesData) {
|
|
@@ -12047,12 +12625,18 @@ ${JSON.stringify(part.output.value)}`
|
|
|
12047
12625
|
}
|
|
12048
12626
|
} else if (message.role === "user") {
|
|
12049
12627
|
const content = extractTextContent(message.content);
|
|
12628
|
+
const images = Array.isArray(message.content) ? message.content.filter((part) => part.type === "image").map((part) => {
|
|
12629
|
+
const data = part.image;
|
|
12630
|
+
const mediaType = part.mediaType || "image/png";
|
|
12631
|
+
return data.startsWith("data:") ? data : `data:${mediaType};base64,${data}`;
|
|
12632
|
+
}) : void 0;
|
|
12050
12633
|
const userMessageData = {
|
|
12051
12634
|
type: "user",
|
|
12052
12635
|
id: message.id || uuid.v4(),
|
|
12053
12636
|
baseDir: this.task.getProjectDir(),
|
|
12054
12637
|
taskId: this.taskId,
|
|
12055
12638
|
content,
|
|
12639
|
+
images: images && images.length > 0 ? images : void 0,
|
|
12056
12640
|
promptContext: message.promptContext
|
|
12057
12641
|
};
|
|
12058
12642
|
messagesData.push(userMessageData);
|
|
@@ -12960,7 +13544,13 @@ class WorktreeManager {
|
|
|
12960
13544
|
await execWithShellPath("git add -A", { cwd: projectPath });
|
|
12961
13545
|
} catch {
|
|
12962
13546
|
}
|
|
12963
|
-
|
|
13547
|
+
try {
|
|
13548
|
+
await execWithShellPath('git commit -m "Initial commit" --allow-empty', { cwd: projectPath });
|
|
13549
|
+
} catch {
|
|
13550
|
+
await execWithShellPath('git -c user.name="AiderDesk" -c user.email="aiderdesk@aiderdesk" commit -m "Initial commit" --allow-empty', {
|
|
13551
|
+
cwd: projectPath
|
|
13552
|
+
});
|
|
13553
|
+
}
|
|
12964
13554
|
}
|
|
12965
13555
|
let baseCommit;
|
|
12966
13556
|
let newBranchName;
|
|
@@ -14114,6 +14704,11 @@ Git output: ${err.stderr || err.stdout || err.message}`
|
|
|
14114
14704
|
logger.warn(`Failed to get files for commit ${commit.hash}:`, commitError);
|
|
14115
14705
|
}
|
|
14116
14706
|
}
|
|
14707
|
+
try {
|
|
14708
|
+
await execWithShellPath("git rev-parse HEAD", { cwd: worktreePath });
|
|
14709
|
+
} catch {
|
|
14710
|
+
return files;
|
|
14711
|
+
}
|
|
14117
14712
|
try {
|
|
14118
14713
|
const { stdout: uncommittedNumstat } = await execWithShellPath("git diff --numstat -z HEAD", {
|
|
14119
14714
|
cwd: worktreePath
|
|
@@ -14212,6 +14807,11 @@ Git output: ${err.stderr || err.stdout || err.message}`
|
|
|
14212
14807
|
* Non-worktree mode: simple uncommitted changes vs HEAD.
|
|
14213
14808
|
*/
|
|
14214
14809
|
async getNonWorktreeUpdatedFiles(worktreePath) {
|
|
14810
|
+
try {
|
|
14811
|
+
await execWithShellPath("git rev-parse HEAD", { cwd: worktreePath });
|
|
14812
|
+
} catch {
|
|
14813
|
+
return [];
|
|
14814
|
+
}
|
|
14215
14815
|
const { stdout } = await execWithShellPath("git diff --numstat -z HEAD", {
|
|
14216
14816
|
cwd: worktreePath
|
|
14217
14817
|
});
|
|
@@ -14515,102 +15115,691 @@ Git output: ${err.stderr || err.stdout || err.message}`
|
|
|
14515
15115
|
}
|
|
14516
15116
|
throw new Error(`Failed to apply uncommitted changes: ${error instanceof Error ? error.message : String(error)}`);
|
|
14517
15117
|
}
|
|
14518
|
-
});
|
|
15118
|
+
});
|
|
15119
|
+
}
|
|
15120
|
+
/**
|
|
15121
|
+
* Revert a merge operation using the stored MergeState
|
|
15122
|
+
* Restores the main branch to its pre-merge state while preserving uncommitted changes
|
|
15123
|
+
*/
|
|
15124
|
+
async revertMerge(projectPath, taskId, worktreePath, mergeState, symlinkFolders = []) {
|
|
15125
|
+
return await withLock(`git-revert-merge-${worktreePath}`, async () => {
|
|
15126
|
+
const timestamp = Date.now();
|
|
15127
|
+
const currentWorktreeStashId = `worktree-${taskId.length > 24 ? taskId.substring(24) : taskId}-revert-${timestamp}`;
|
|
15128
|
+
const currentMainStashId = `main-${taskId.length > 24 ? taskId.substring(24) : taskId}-revert-${timestamp}`;
|
|
15129
|
+
let worktreeRevertStashId = null;
|
|
15130
|
+
let mainRevertStashId = null;
|
|
15131
|
+
try {
|
|
15132
|
+
logger.info("Starting merge revert operation", { mergeState });
|
|
15133
|
+
worktreeRevertStashId = await this.stashUncommittedChanges(
|
|
15134
|
+
currentWorktreeStashId,
|
|
15135
|
+
worktreePath,
|
|
15136
|
+
"Current uncommitted changes before revert",
|
|
15137
|
+
symlinkFolders
|
|
15138
|
+
);
|
|
15139
|
+
mainRevertStashId = await this.stashUncommittedChanges(currentMainStashId, projectPath, "Current uncommitted changes before revert", []);
|
|
15140
|
+
const targetBranch = mergeState.targetBranch || await this.getProjectMainBranch(projectPath);
|
|
15141
|
+
await execWithShellPath(`git checkout ${targetBranch}`, {
|
|
15142
|
+
cwd: projectPath
|
|
15143
|
+
});
|
|
15144
|
+
logger.info(`Resetting ${targetBranch} branch to ${mergeState.beforeMergeCommitHash}`);
|
|
15145
|
+
await execWithShellPath(`git reset --hard ${mergeState.beforeMergeCommitHash}`, { cwd: projectPath });
|
|
15146
|
+
logger.info(`Resetting worktree branch to ${mergeState.worktreeBranchCommitHash}`);
|
|
15147
|
+
await execWithShellPath(`git reset --hard ${mergeState.worktreeBranchCommitHash}`, { cwd: worktreePath });
|
|
15148
|
+
if (mergeState.mainOriginalStashId) {
|
|
15149
|
+
logger.info("Restoring main branch original uncommitted changes");
|
|
15150
|
+
await this.applyStash(projectPath, mergeState.mainOriginalStashId);
|
|
15151
|
+
await this.dropStash(projectPath, mergeState.mainOriginalStashId);
|
|
15152
|
+
}
|
|
15153
|
+
if (worktreeRevertStashId) {
|
|
15154
|
+
logger.info("Restoring uncommitted changes in worktree");
|
|
15155
|
+
await this.applyStash(worktreePath, currentWorktreeStashId);
|
|
15156
|
+
await this.dropStash(worktreePath, currentWorktreeStashId);
|
|
15157
|
+
}
|
|
15158
|
+
if (mainRevertStashId) {
|
|
15159
|
+
await this.dropStash(projectPath, currentMainStashId);
|
|
15160
|
+
}
|
|
15161
|
+
logger.info("Merge revert completed successfully");
|
|
15162
|
+
} catch (error) {
|
|
15163
|
+
logger.error("Failed to revert merge:", error);
|
|
15164
|
+
if (worktreeRevertStashId) {
|
|
15165
|
+
try {
|
|
15166
|
+
await this.applyStash(worktreePath, currentWorktreeStashId);
|
|
15167
|
+
await this.dropStash(worktreePath, currentWorktreeStashId);
|
|
15168
|
+
} catch (recoveryError) {
|
|
15169
|
+
logger.error("Failed to recover worktree stash:", recoveryError);
|
|
15170
|
+
}
|
|
15171
|
+
}
|
|
15172
|
+
if (mainRevertStashId) {
|
|
15173
|
+
try {
|
|
15174
|
+
await this.applyStash(projectPath, currentMainStashId);
|
|
15175
|
+
await this.dropStash(projectPath, currentMainStashId);
|
|
15176
|
+
} catch (recoveryError) {
|
|
15177
|
+
logger.error("Failed to recover main branch stash:", recoveryError);
|
|
15178
|
+
}
|
|
15179
|
+
}
|
|
15180
|
+
throw new Error(`Failed to revert merge: ${error instanceof Error ? error.message : String(error)}`);
|
|
15181
|
+
}
|
|
15182
|
+
});
|
|
15183
|
+
}
|
|
15184
|
+
async pruneDeleted(projectDir) {
|
|
15185
|
+
logger.info("Pruning deleted worktrees", {
|
|
15186
|
+
projectDir
|
|
15187
|
+
});
|
|
15188
|
+
const worktrees = await this.listWorktrees(projectDir);
|
|
15189
|
+
logger.debug("Found worktrees", {
|
|
15190
|
+
worktrees
|
|
15191
|
+
});
|
|
15192
|
+
for (const worktree of worktrees) {
|
|
15193
|
+
if (worktree.path.startsWith(path.join(projectDir, AIDER_DESK_TASKS_DIR)) && worktree.prunable) {
|
|
15194
|
+
try {
|
|
15195
|
+
logger.debug(`Pruning deleted worktree: ${worktree.path}`);
|
|
15196
|
+
await execWithShellPath(`git worktree remove ${worktree.path}`, {
|
|
15197
|
+
cwd: projectDir
|
|
15198
|
+
});
|
|
15199
|
+
} catch (error) {
|
|
15200
|
+
logger.warn("Failed to prune worktree:", {
|
|
15201
|
+
worktree,
|
|
15202
|
+
error: error instanceof Error ? error.message : String(error)
|
|
15203
|
+
});
|
|
15204
|
+
await fs.rm(worktree.path, { recursive: true, force: true });
|
|
15205
|
+
}
|
|
15206
|
+
}
|
|
15207
|
+
}
|
|
15208
|
+
}
|
|
15209
|
+
async close(projectDir) {
|
|
15210
|
+
logger.info("Closing worktree manager");
|
|
15211
|
+
await this.pruneDeleted(projectDir);
|
|
15212
|
+
}
|
|
15213
|
+
}
|
|
15214
|
+
const extractFilePath = (args) => {
|
|
15215
|
+
if (typeof args === "object" && args !== null && "filePath" in args) {
|
|
15216
|
+
return args.filePath;
|
|
15217
|
+
}
|
|
15218
|
+
return void 0;
|
|
15219
|
+
};
|
|
15220
|
+
const getToolOutputText = (output) => {
|
|
15221
|
+
if (!output) {
|
|
15222
|
+
return "";
|
|
15223
|
+
}
|
|
15224
|
+
if (output.type === "text") {
|
|
15225
|
+
return output.value;
|
|
15226
|
+
}
|
|
15227
|
+
if (output.type === "error-text") {
|
|
15228
|
+
return output.value;
|
|
15229
|
+
}
|
|
15230
|
+
return JSON.stringify(output.value);
|
|
15231
|
+
};
|
|
15232
|
+
const isErrorResult = (output) => {
|
|
15233
|
+
const text = getToolOutputText(output).toLowerCase();
|
|
15234
|
+
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");
|
|
15235
|
+
};
|
|
15236
|
+
const isNoOpResult = (output) => {
|
|
15237
|
+
const text = getToolOutputText(output);
|
|
15238
|
+
return text.includes("Already updated - no changes were needed");
|
|
15239
|
+
};
|
|
15240
|
+
const isPowerTool = (serverName) => {
|
|
15241
|
+
return serverName === POWER_TOOL_GROUP_NAME;
|
|
15242
|
+
};
|
|
15243
|
+
const getToolInfoFromToolMessage = (message) => {
|
|
15244
|
+
return message.content.filter((part) => part.type === "tool-result").map((part) => {
|
|
15245
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15246
|
+
return {
|
|
15247
|
+
messageIndex: -1,
|
|
15248
|
+
toolCallId: part.toolCallId,
|
|
15249
|
+
toolName,
|
|
15250
|
+
serverName,
|
|
15251
|
+
input: void 0,
|
|
15252
|
+
output: part.output
|
|
15253
|
+
};
|
|
15254
|
+
});
|
|
15255
|
+
};
|
|
15256
|
+
const findAssistantToolCall = (messages, toolCallId) => {
|
|
15257
|
+
for (let i = 0; i < messages.length; i++) {
|
|
15258
|
+
const msg = messages[i];
|
|
15259
|
+
if (msg.role !== "assistant") {
|
|
15260
|
+
continue;
|
|
15261
|
+
}
|
|
15262
|
+
if (!Array.isArray(msg.content)) {
|
|
15263
|
+
continue;
|
|
15264
|
+
}
|
|
15265
|
+
for (let j = 0; j < msg.content.length; j++) {
|
|
15266
|
+
const part = msg.content[j];
|
|
15267
|
+
if (part.type === "tool-call" && part.toolCallId === toolCallId) {
|
|
15268
|
+
const [, toolName] = extractServerNameToolName(part.toolName);
|
|
15269
|
+
return {
|
|
15270
|
+
messageId: msg.id,
|
|
15271
|
+
messageIndex: i,
|
|
15272
|
+
partIndex: j,
|
|
15273
|
+
toolCallId: part.toolCallId,
|
|
15274
|
+
toolName,
|
|
15275
|
+
input: part.input
|
|
15276
|
+
};
|
|
15277
|
+
}
|
|
15278
|
+
}
|
|
15279
|
+
}
|
|
15280
|
+
return void 0;
|
|
15281
|
+
};
|
|
15282
|
+
const cloneMessages = (messages) => {
|
|
15283
|
+
return messages.map((msg) => {
|
|
15284
|
+
if (msg.role === "tool") {
|
|
15285
|
+
return {
|
|
15286
|
+
...msg,
|
|
15287
|
+
content: msg.content.map((part) => ({ ...part }))
|
|
15288
|
+
};
|
|
15289
|
+
}
|
|
15290
|
+
return {
|
|
15291
|
+
...msg,
|
|
15292
|
+
content: Array.isArray(msg.content) ? msg.content.map((part) => ({ ...part })) : msg.content
|
|
15293
|
+
};
|
|
15294
|
+
});
|
|
15295
|
+
};
|
|
15296
|
+
const removeToolCallFromAssistant = (messages, toolCallId) => {
|
|
15297
|
+
const callInfo = findAssistantToolCall(messages, toolCallId);
|
|
15298
|
+
if (!callInfo) {
|
|
15299
|
+
return;
|
|
15300
|
+
}
|
|
15301
|
+
const assistantMsg = messages[callInfo.messageIndex];
|
|
15302
|
+
if (!Array.isArray(assistantMsg.content)) {
|
|
15303
|
+
return;
|
|
15304
|
+
}
|
|
15305
|
+
assistantMsg.content.splice(callInfo.partIndex, 1);
|
|
15306
|
+
const hasToolCalls = assistantMsg.content.some((p) => p.type === "tool-call");
|
|
15307
|
+
if (!hasToolCalls) {
|
|
15308
|
+
messages.splice(callInfo.messageIndex, 1);
|
|
15309
|
+
}
|
|
15310
|
+
};
|
|
15311
|
+
const removeToolResult = (messages, toolCallId) => {
|
|
15312
|
+
const toolMsgIdx = messages.findIndex((m) => m.role === "tool" && m.content.some((p) => p.type === "tool-result" && p.toolCallId === toolCallId));
|
|
15313
|
+
if (toolMsgIdx === -1) {
|
|
15314
|
+
return;
|
|
15315
|
+
}
|
|
15316
|
+
const toolMsg = messages[toolMsgIdx];
|
|
15317
|
+
const partIdx = toolMsg.content.findIndex((p) => p.type === "tool-result" && p.toolCallId === toolCallId);
|
|
15318
|
+
if (partIdx === -1) {
|
|
15319
|
+
return;
|
|
15320
|
+
}
|
|
15321
|
+
toolMsg.content.splice(partIdx, 1);
|
|
15322
|
+
if (toolMsg.content.length === 0) {
|
|
15323
|
+
messages.splice(toolMsgIdx, 1);
|
|
15324
|
+
}
|
|
15325
|
+
};
|
|
15326
|
+
const getProtectedStartIndex = (messages, protectedMessageCount) => {
|
|
15327
|
+
return Math.max(0, messages.length - protectedMessageCount);
|
|
15328
|
+
};
|
|
15329
|
+
const mergeConsecutiveAssistantMessages = (messages) => {
|
|
15330
|
+
const result = cloneMessages(messages);
|
|
15331
|
+
for (let i = result.length - 2; i >= 0; i--) {
|
|
15332
|
+
const current = result[i];
|
|
15333
|
+
const next = result[i + 1];
|
|
15334
|
+
if (current.role !== "assistant" || next.role !== "assistant") {
|
|
15335
|
+
continue;
|
|
15336
|
+
}
|
|
15337
|
+
const currentHasToolCalls = Array.isArray(current.content) && current.content.some((p) => p.type === "tool-call");
|
|
15338
|
+
const nextHasToolCalls = Array.isArray(next.content) && next.content.some((p) => p.type === "tool-call");
|
|
15339
|
+
if (currentHasToolCalls || nextHasToolCalls) {
|
|
15340
|
+
continue;
|
|
15341
|
+
}
|
|
15342
|
+
const currentTextParts = Array.isArray(current.content) ? current.content.filter((p) => p.type === "text") : [];
|
|
15343
|
+
const nextTextParts = Array.isArray(next.content) ? next.content.filter((p) => p.type === "text") : [];
|
|
15344
|
+
const mergedText = [...currentTextParts, ...nextTextParts].map((p) => p.text).filter(Boolean).join("\n\n");
|
|
15345
|
+
current.content = mergedText ? [{ type: "text", text: mergedText }] : [];
|
|
15346
|
+
result.splice(i + 1, 1);
|
|
15347
|
+
}
|
|
15348
|
+
return result.filter((msg) => {
|
|
15349
|
+
if (msg.role === "assistant" && Array.isArray(msg.content) && msg.content.length === 0) {
|
|
15350
|
+
return false;
|
|
15351
|
+
}
|
|
15352
|
+
return true;
|
|
15353
|
+
});
|
|
15354
|
+
};
|
|
15355
|
+
const smartCompactMessages = (messages, protectedMessageCount = 10) => {
|
|
15356
|
+
let result = cloneMessages(messages);
|
|
15357
|
+
result = removeErroredTools(result, protectedMessageCount);
|
|
15358
|
+
result = collapseFileEdits(result, protectedMessageCount);
|
|
15359
|
+
result = removeStaleFileReads(result, protectedMessageCount);
|
|
15360
|
+
result = removeObsoleteSearches(result, protectedMessageCount);
|
|
15361
|
+
result = compactSemanticSearches(result, protectedMessageCount);
|
|
15362
|
+
result = deduplicateBash(result, protectedMessageCount);
|
|
15363
|
+
result = redactFetchOutputs(result, protectedMessageCount);
|
|
15364
|
+
result = mergeConsecutiveAssistantMessages(result);
|
|
15365
|
+
return result;
|
|
15366
|
+
};
|
|
15367
|
+
const removeErroredTools = (messages, protectedMessageCount = 10) => {
|
|
15368
|
+
const result = cloneMessages(messages);
|
|
15369
|
+
const protectedStart = getProtectedStartIndex(result, protectedMessageCount);
|
|
15370
|
+
for (let i = protectedStart - 1; i >= 0; i--) {
|
|
15371
|
+
if (i >= result.length) {
|
|
15372
|
+
continue;
|
|
15373
|
+
}
|
|
15374
|
+
const msg = result[i];
|
|
15375
|
+
if (msg.role !== "tool") {
|
|
15376
|
+
continue;
|
|
15377
|
+
}
|
|
15378
|
+
const toolInfos = getToolInfoFromToolMessage(msg);
|
|
15379
|
+
for (const info of toolInfos) {
|
|
15380
|
+
if (!isPowerTool(info.serverName)) {
|
|
15381
|
+
continue;
|
|
15382
|
+
}
|
|
15383
|
+
if (isErrorResult(info.output) || isNoOpResult(info.output)) {
|
|
15384
|
+
removeToolCallFromAssistant(result, info.toolCallId);
|
|
15385
|
+
removeToolResult(result, info.toolCallId);
|
|
15386
|
+
}
|
|
15387
|
+
}
|
|
15388
|
+
}
|
|
15389
|
+
return result;
|
|
15390
|
+
};
|
|
15391
|
+
const collapseFileEdits = (messages, protectedMessageCount = 10) => {
|
|
15392
|
+
const result = cloneMessages(messages);
|
|
15393
|
+
const protectedStart = getProtectedStartIndex(result, protectedMessageCount);
|
|
15394
|
+
const fileEditGroups = /* @__PURE__ */ new Map();
|
|
15395
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15396
|
+
if (i >= result.length) {
|
|
15397
|
+
break;
|
|
15398
|
+
}
|
|
15399
|
+
const msg = result[i];
|
|
15400
|
+
if (msg.role !== "tool") {
|
|
15401
|
+
continue;
|
|
15402
|
+
}
|
|
15403
|
+
for (const part of msg.content) {
|
|
15404
|
+
if (part.type !== "tool-result") {
|
|
15405
|
+
continue;
|
|
15406
|
+
}
|
|
15407
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15408
|
+
if (!isPowerTool(serverName)) {
|
|
15409
|
+
continue;
|
|
15410
|
+
}
|
|
15411
|
+
if (toolName !== POWER_TOOL_FILE_EDIT && toolName !== POWER_TOOL_FILE_WRITE) {
|
|
15412
|
+
continue;
|
|
15413
|
+
}
|
|
15414
|
+
const callInfo = findAssistantToolCall(result, part.toolCallId);
|
|
15415
|
+
if (!callInfo) {
|
|
15416
|
+
continue;
|
|
15417
|
+
}
|
|
15418
|
+
const filePath = extractFilePath(callInfo.input);
|
|
15419
|
+
if (!filePath) {
|
|
15420
|
+
continue;
|
|
15421
|
+
}
|
|
15422
|
+
if (!fileEditGroups.has(filePath)) {
|
|
15423
|
+
fileEditGroups.set(filePath, []);
|
|
15424
|
+
}
|
|
15425
|
+
fileEditGroups.get(filePath).push({
|
|
15426
|
+
assistantMessageId: callInfo.messageId,
|
|
15427
|
+
toolCallId: part.toolCallId
|
|
15428
|
+
});
|
|
15429
|
+
}
|
|
15430
|
+
}
|
|
15431
|
+
for (const [filePath, edits] of fileEditGroups) {
|
|
15432
|
+
if (edits.length === 0) {
|
|
15433
|
+
continue;
|
|
15434
|
+
}
|
|
15435
|
+
const lastEdit = edits[edits.length - 1];
|
|
15436
|
+
const syntheticMessage = {
|
|
15437
|
+
id: uuid.v4(),
|
|
15438
|
+
role: "assistant",
|
|
15439
|
+
content: [
|
|
15440
|
+
{
|
|
15441
|
+
type: "text",
|
|
15442
|
+
text: `<file-edited path="${filePath}">File was edited. Read the content again if you need to work on it.</file-edited>`
|
|
15443
|
+
}
|
|
15444
|
+
]
|
|
15445
|
+
};
|
|
15446
|
+
const assistantIndex = result.findIndex((m) => m.id === lastEdit.assistantMessageId);
|
|
15447
|
+
const insertIndex = assistantIndex !== -1 ? assistantIndex + 1 : 0;
|
|
15448
|
+
result.splice(insertIndex, 0, syntheticMessage);
|
|
15449
|
+
for (const edit of edits) {
|
|
15450
|
+
removeToolCallFromAssistant(result, edit.toolCallId);
|
|
15451
|
+
removeToolResult(result, edit.toolCallId);
|
|
15452
|
+
}
|
|
15453
|
+
}
|
|
15454
|
+
return result;
|
|
15455
|
+
};
|
|
15456
|
+
const removeStaleFileReads = (messages, protectedMessageCount = 10) => {
|
|
15457
|
+
const result = cloneMessages(messages);
|
|
15458
|
+
const protectedStart = getProtectedStartIndex(result, protectedMessageCount);
|
|
15459
|
+
const editedFilePaths = /* @__PURE__ */ new Set();
|
|
15460
|
+
for (let i = 0; i < result.length; i++) {
|
|
15461
|
+
const msg = result[i];
|
|
15462
|
+
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
15463
|
+
for (const part of msg.content) {
|
|
15464
|
+
if (part.type === "text") {
|
|
15465
|
+
const match = part.text.match(/<file-edited path="([^"]+)">/);
|
|
15466
|
+
if (match) {
|
|
15467
|
+
editedFilePaths.add(match[1]);
|
|
15468
|
+
}
|
|
15469
|
+
}
|
|
15470
|
+
}
|
|
15471
|
+
continue;
|
|
15472
|
+
}
|
|
15473
|
+
if (msg.role !== "tool") {
|
|
15474
|
+
continue;
|
|
15475
|
+
}
|
|
15476
|
+
for (const part of msg.content) {
|
|
15477
|
+
if (part.type !== "tool-result") {
|
|
15478
|
+
continue;
|
|
15479
|
+
}
|
|
15480
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15481
|
+
if (!isPowerTool(serverName)) {
|
|
15482
|
+
continue;
|
|
15483
|
+
}
|
|
15484
|
+
if (toolName !== POWER_TOOL_FILE_EDIT && toolName !== POWER_TOOL_FILE_WRITE) {
|
|
15485
|
+
continue;
|
|
15486
|
+
}
|
|
15487
|
+
const callInfo = findAssistantToolCall(result, part.toolCallId);
|
|
15488
|
+
if (callInfo) {
|
|
15489
|
+
const filePath = extractFilePath(callInfo.input);
|
|
15490
|
+
if (filePath) {
|
|
15491
|
+
editedFilePaths.add(filePath);
|
|
15492
|
+
}
|
|
15493
|
+
}
|
|
15494
|
+
}
|
|
15495
|
+
}
|
|
15496
|
+
const protectedReadFilePaths = /* @__PURE__ */ new Set();
|
|
15497
|
+
for (let i = protectedStart; i < result.length; i++) {
|
|
15498
|
+
const msg = result[i];
|
|
15499
|
+
if (msg.role !== "tool") {
|
|
15500
|
+
continue;
|
|
15501
|
+
}
|
|
15502
|
+
for (const part of msg.content) {
|
|
15503
|
+
if (part.type !== "tool-result") {
|
|
15504
|
+
continue;
|
|
15505
|
+
}
|
|
15506
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15507
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_FILE_READ) {
|
|
15508
|
+
continue;
|
|
15509
|
+
}
|
|
15510
|
+
const callInfo = findAssistantToolCall(result, part.toolCallId);
|
|
15511
|
+
if (callInfo) {
|
|
15512
|
+
const filePath = extractFilePath(callInfo.input);
|
|
15513
|
+
if (filePath) {
|
|
15514
|
+
protectedReadFilePaths.add(filePath);
|
|
15515
|
+
}
|
|
15516
|
+
}
|
|
15517
|
+
}
|
|
15518
|
+
}
|
|
15519
|
+
const readFileGroups = /* @__PURE__ */ new Map();
|
|
15520
|
+
for (let i = protectedStart - 1; i >= 0; i--) {
|
|
15521
|
+
if (i >= result.length) {
|
|
15522
|
+
continue;
|
|
15523
|
+
}
|
|
15524
|
+
const msg = result[i];
|
|
15525
|
+
if (msg.role !== "tool") {
|
|
15526
|
+
continue;
|
|
15527
|
+
}
|
|
15528
|
+
for (const part of msg.content) {
|
|
15529
|
+
if (part.type !== "tool-result") {
|
|
15530
|
+
continue;
|
|
15531
|
+
}
|
|
15532
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15533
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_FILE_READ) {
|
|
15534
|
+
continue;
|
|
15535
|
+
}
|
|
15536
|
+
const callInfo = findAssistantToolCall(result, part.toolCallId);
|
|
15537
|
+
if (!callInfo) {
|
|
15538
|
+
continue;
|
|
15539
|
+
}
|
|
15540
|
+
const filePath = extractFilePath(callInfo.input);
|
|
15541
|
+
if (!filePath) {
|
|
15542
|
+
continue;
|
|
15543
|
+
}
|
|
15544
|
+
if (editedFilePaths.has(filePath) || protectedReadFilePaths.has(filePath)) {
|
|
15545
|
+
removeToolCallFromAssistant(result, part.toolCallId);
|
|
15546
|
+
removeToolResult(result, part.toolCallId);
|
|
15547
|
+
continue;
|
|
15548
|
+
}
|
|
15549
|
+
if (!readFileGroups.has(filePath)) {
|
|
15550
|
+
readFileGroups.set(filePath, []);
|
|
15551
|
+
}
|
|
15552
|
+
readFileGroups.get(filePath).push({
|
|
15553
|
+
messageIndex: i,
|
|
15554
|
+
toolCallId: part.toolCallId
|
|
15555
|
+
});
|
|
15556
|
+
}
|
|
15557
|
+
}
|
|
15558
|
+
for (const [, reads] of readFileGroups) {
|
|
15559
|
+
if (reads.length <= 1) {
|
|
15560
|
+
continue;
|
|
15561
|
+
}
|
|
15562
|
+
const toRemove = reads.slice(0, -1);
|
|
15563
|
+
for (const read of toRemove) {
|
|
15564
|
+
removeToolCallFromAssistant(result, read.toolCallId);
|
|
15565
|
+
removeToolResult(result, read.toolCallId);
|
|
15566
|
+
}
|
|
15567
|
+
}
|
|
15568
|
+
return result;
|
|
15569
|
+
};
|
|
15570
|
+
const removeObsoleteSearches = (messages, protectedMessageCount = 10) => {
|
|
15571
|
+
const result = cloneMessages(messages);
|
|
15572
|
+
const protectedStart = getProtectedStartIndex(result, protectedMessageCount);
|
|
15573
|
+
const fileModificationPositions = [];
|
|
15574
|
+
for (let i = 0; i < result.length; i++) {
|
|
15575
|
+
const msg = result[i];
|
|
15576
|
+
if (msg.role === "assistant" && Array.isArray(msg.content)) {
|
|
15577
|
+
for (const part of msg.content) {
|
|
15578
|
+
if (part.type === "text" && part.text.includes("<file-edited")) {
|
|
15579
|
+
fileModificationPositions.push(i);
|
|
15580
|
+
break;
|
|
15581
|
+
}
|
|
15582
|
+
}
|
|
15583
|
+
continue;
|
|
15584
|
+
}
|
|
15585
|
+
if (msg.role !== "tool") {
|
|
15586
|
+
continue;
|
|
15587
|
+
}
|
|
15588
|
+
for (const part of msg.content) {
|
|
15589
|
+
if (part.type !== "tool-result") {
|
|
15590
|
+
continue;
|
|
15591
|
+
}
|
|
15592
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15593
|
+
if (!isPowerTool(serverName)) {
|
|
15594
|
+
continue;
|
|
15595
|
+
}
|
|
15596
|
+
if (toolName === POWER_TOOL_FILE_EDIT || toolName === POWER_TOOL_FILE_WRITE) {
|
|
15597
|
+
fileModificationPositions.push(i);
|
|
15598
|
+
}
|
|
15599
|
+
}
|
|
15600
|
+
}
|
|
15601
|
+
const hasFileModifications = fileModificationPositions.length > 0;
|
|
15602
|
+
if (!hasFileModifications) {
|
|
15603
|
+
return result;
|
|
15604
|
+
}
|
|
15605
|
+
for (let i = protectedStart - 1; i >= 0; i--) {
|
|
15606
|
+
if (i >= result.length) {
|
|
15607
|
+
continue;
|
|
15608
|
+
}
|
|
15609
|
+
const msg = result[i];
|
|
15610
|
+
if (msg.role !== "tool") {
|
|
15611
|
+
continue;
|
|
15612
|
+
}
|
|
15613
|
+
for (const part of msg.content) {
|
|
15614
|
+
if (part.type !== "tool-result") {
|
|
15615
|
+
continue;
|
|
15616
|
+
}
|
|
15617
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15618
|
+
if (!isPowerTool(serverName)) {
|
|
15619
|
+
continue;
|
|
15620
|
+
}
|
|
15621
|
+
if (toolName !== POWER_TOOL_GLOB && toolName !== POWER_TOOL_GREP) {
|
|
15622
|
+
continue;
|
|
15623
|
+
}
|
|
15624
|
+
const hasLaterModification = fileModificationPositions.some((pos) => pos > i);
|
|
15625
|
+
if (hasLaterModification) {
|
|
15626
|
+
removeToolCallFromAssistant(result, part.toolCallId);
|
|
15627
|
+
removeToolResult(result, part.toolCallId);
|
|
15628
|
+
}
|
|
15629
|
+
}
|
|
14519
15630
|
}
|
|
14520
|
-
|
|
14521
|
-
|
|
14522
|
-
|
|
14523
|
-
|
|
14524
|
-
|
|
14525
|
-
|
|
14526
|
-
|
|
14527
|
-
|
|
14528
|
-
|
|
14529
|
-
|
|
14530
|
-
|
|
15631
|
+
return result;
|
|
15632
|
+
};
|
|
15633
|
+
const compactSemanticSearches = (messages, protectedMessageCount = 10) => {
|
|
15634
|
+
const result = cloneMessages(messages);
|
|
15635
|
+
const protectedStart = getProtectedStartIndex(result, protectedMessageCount);
|
|
15636
|
+
const searchIndices = [];
|
|
15637
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15638
|
+
if (i >= result.length) {
|
|
15639
|
+
break;
|
|
15640
|
+
}
|
|
15641
|
+
const msg = result[i];
|
|
15642
|
+
if (msg.role !== "tool") {
|
|
15643
|
+
continue;
|
|
15644
|
+
}
|
|
15645
|
+
for (let j = 0; j < msg.content.length; j++) {
|
|
15646
|
+
const part = msg.content[j];
|
|
15647
|
+
if (part.type !== "tool-result") {
|
|
15648
|
+
continue;
|
|
15649
|
+
}
|
|
15650
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15651
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_SEMANTIC_SEARCH) {
|
|
15652
|
+
continue;
|
|
15653
|
+
}
|
|
15654
|
+
searchIndices.push({ messageIndex: i, partIndex: j, toolCallId: part.toolCallId });
|
|
15655
|
+
}
|
|
15656
|
+
}
|
|
15657
|
+
if (searchIndices.length <= 1) {
|
|
15658
|
+
return result;
|
|
15659
|
+
}
|
|
15660
|
+
const toRemove = searchIndices.slice(0, -1);
|
|
15661
|
+
const toKeep = searchIndices[searchIndices.length - 1];
|
|
15662
|
+
for (const search2 of toRemove) {
|
|
15663
|
+
removeToolCallFromAssistant(result, search2.toolCallId);
|
|
15664
|
+
removeToolResult(result, search2.toolCallId);
|
|
15665
|
+
}
|
|
15666
|
+
const keptToolIdx = result.findIndex((m) => m.role === "tool" && m.content.some((p) => p.type === "tool-result" && p.toolCallId === toKeep.toolCallId));
|
|
15667
|
+
if (keptToolIdx !== -1) {
|
|
15668
|
+
const keptMsg = result[keptToolIdx];
|
|
15669
|
+
const keptPartIdx = keptMsg.content.findIndex((p) => p.type === "tool-result" && p.toolCallId === toKeep.toolCallId);
|
|
15670
|
+
const keptPart = keptMsg.content[keptPartIdx];
|
|
15671
|
+
if (keptPart && keptPart.output.type === "text") {
|
|
15672
|
+
const lines = keptPart.output.value.split("\n");
|
|
15673
|
+
if (lines.length > 50) {
|
|
15674
|
+
keptPart.output = {
|
|
15675
|
+
type: "text",
|
|
15676
|
+
value: lines.slice(0, 50).join("\n") + "\n<truncated due to compaction, run again if full output is needed>"
|
|
15677
|
+
};
|
|
15678
|
+
}
|
|
15679
|
+
}
|
|
15680
|
+
}
|
|
15681
|
+
return result;
|
|
15682
|
+
};
|
|
15683
|
+
const deduplicateBash = (messages, protectedMessageCount = 10) => {
|
|
15684
|
+
const result = cloneMessages(messages);
|
|
15685
|
+
const protectedStart = getProtectedStartIndex(result, protectedMessageCount);
|
|
15686
|
+
const bashCommands = /* @__PURE__ */ new Map();
|
|
15687
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15688
|
+
const msg = result[i];
|
|
15689
|
+
if (msg.role !== "tool") {
|
|
15690
|
+
continue;
|
|
15691
|
+
}
|
|
15692
|
+
for (const part of msg.content) {
|
|
15693
|
+
if (part.type !== "tool-result") {
|
|
15694
|
+
continue;
|
|
15695
|
+
}
|
|
15696
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15697
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_BASH) {
|
|
15698
|
+
continue;
|
|
15699
|
+
}
|
|
15700
|
+
const callInfo = findAssistantToolCall(result, part.toolCallId);
|
|
15701
|
+
if (!callInfo) {
|
|
15702
|
+
continue;
|
|
15703
|
+
}
|
|
15704
|
+
const command = typeof callInfo.input?.command === "string" ? callInfo.input.command.trim() : null;
|
|
15705
|
+
if (!command) {
|
|
15706
|
+
continue;
|
|
15707
|
+
}
|
|
15708
|
+
if (!bashCommands.has(command)) {
|
|
15709
|
+
bashCommands.set(command, []);
|
|
15710
|
+
}
|
|
15711
|
+
bashCommands.get(command).push({
|
|
15712
|
+
messageIndex: i,
|
|
15713
|
+
toolCallId: part.toolCallId
|
|
15714
|
+
});
|
|
15715
|
+
}
|
|
15716
|
+
}
|
|
15717
|
+
for (const [, occurrences] of bashCommands) {
|
|
15718
|
+
if (occurrences.length <= 1) {
|
|
15719
|
+
continue;
|
|
15720
|
+
}
|
|
15721
|
+
const toRemove = occurrences.slice(0, -1);
|
|
15722
|
+
for (const occ of toRemove) {
|
|
15723
|
+
removeToolCallFromAssistant(result, occ.toolCallId);
|
|
15724
|
+
removeToolResult(result, occ.toolCallId);
|
|
15725
|
+
}
|
|
15726
|
+
}
|
|
15727
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15728
|
+
if (i >= result.length) {
|
|
15729
|
+
break;
|
|
15730
|
+
}
|
|
15731
|
+
const msg = result[i];
|
|
15732
|
+
if (msg.role !== "tool") {
|
|
15733
|
+
continue;
|
|
15734
|
+
}
|
|
15735
|
+
for (let j = 0; j < msg.content.length; j++) {
|
|
15736
|
+
const part = msg.content[j];
|
|
15737
|
+
if (part.type !== "tool-result") {
|
|
15738
|
+
continue;
|
|
15739
|
+
}
|
|
15740
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15741
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_BASH) {
|
|
15742
|
+
continue;
|
|
15743
|
+
}
|
|
15744
|
+
if (part.output.type !== "text") {
|
|
15745
|
+
continue;
|
|
15746
|
+
}
|
|
14531
15747
|
try {
|
|
14532
|
-
|
|
14533
|
-
|
|
14534
|
-
|
|
14535
|
-
|
|
14536
|
-
"
|
|
14537
|
-
|
|
14538
|
-
|
|
14539
|
-
|
|
14540
|
-
|
|
14541
|
-
await execWithShellPath(`git checkout ${targetBranch}`, {
|
|
14542
|
-
cwd: projectPath
|
|
14543
|
-
});
|
|
14544
|
-
logger.info(`Resetting ${targetBranch} branch to ${mergeState.beforeMergeCommitHash}`);
|
|
14545
|
-
await execWithShellPath(`git reset --hard ${mergeState.beforeMergeCommitHash}`, { cwd: projectPath });
|
|
14546
|
-
logger.info(`Resetting worktree branch to ${mergeState.worktreeBranchCommitHash}`);
|
|
14547
|
-
await execWithShellPath(`git reset --hard ${mergeState.worktreeBranchCommitHash}`, { cwd: worktreePath });
|
|
14548
|
-
if (mergeState.mainOriginalStashId) {
|
|
14549
|
-
logger.info("Restoring main branch original uncommitted changes");
|
|
14550
|
-
await this.applyStash(projectPath, mergeState.mainOriginalStashId);
|
|
14551
|
-
await this.dropStash(projectPath, mergeState.mainOriginalStashId);
|
|
14552
|
-
}
|
|
14553
|
-
if (worktreeRevertStashId) {
|
|
14554
|
-
logger.info("Restoring uncommitted changes in worktree");
|
|
14555
|
-
await this.applyStash(worktreePath, currentWorktreeStashId);
|
|
14556
|
-
await this.dropStash(worktreePath, currentWorktreeStashId);
|
|
14557
|
-
}
|
|
14558
|
-
if (mainRevertStashId) {
|
|
14559
|
-
await this.dropStash(projectPath, currentMainStashId);
|
|
14560
|
-
}
|
|
14561
|
-
logger.info("Merge revert completed successfully");
|
|
14562
|
-
} catch (error) {
|
|
14563
|
-
logger.error("Failed to revert merge:", error);
|
|
14564
|
-
if (worktreeRevertStashId) {
|
|
14565
|
-
try {
|
|
14566
|
-
await this.applyStash(worktreePath, currentWorktreeStashId);
|
|
14567
|
-
await this.dropStash(worktreePath, currentWorktreeStashId);
|
|
14568
|
-
} catch (recoveryError) {
|
|
14569
|
-
logger.error("Failed to recover worktree stash:", recoveryError);
|
|
15748
|
+
const parsed = JSON.parse(part.output.value);
|
|
15749
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
15750
|
+
const stdout = typeof parsed.stdout === "string" ? parsed.stdout : "";
|
|
15751
|
+
const stderr = typeof parsed.stderr === "string" ? parsed.stderr : "";
|
|
15752
|
+
const redactionMessage = "<output redacted due to compaction, run again if output is needed>";
|
|
15753
|
+
let modified = false;
|
|
15754
|
+
if (stdout.length > 30) {
|
|
15755
|
+
parsed.stdout = redactionMessage;
|
|
15756
|
+
modified = true;
|
|
14570
15757
|
}
|
|
14571
|
-
|
|
14572
|
-
|
|
14573
|
-
|
|
14574
|
-
|
|
14575
|
-
|
|
14576
|
-
|
|
14577
|
-
|
|
15758
|
+
if (stderr.length > 30) {
|
|
15759
|
+
parsed.stderr = redactionMessage;
|
|
15760
|
+
modified = true;
|
|
15761
|
+
}
|
|
15762
|
+
if (modified) {
|
|
15763
|
+
part.output = {
|
|
15764
|
+
type: "text",
|
|
15765
|
+
value: JSON.stringify(parsed)
|
|
15766
|
+
};
|
|
14578
15767
|
}
|
|
14579
15768
|
}
|
|
14580
|
-
|
|
15769
|
+
} catch {
|
|
14581
15770
|
}
|
|
14582
|
-
}
|
|
15771
|
+
}
|
|
14583
15772
|
}
|
|
14584
|
-
|
|
14585
|
-
|
|
14586
|
-
|
|
14587
|
-
|
|
14588
|
-
|
|
14589
|
-
|
|
14590
|
-
|
|
14591
|
-
|
|
14592
|
-
|
|
14593
|
-
|
|
14594
|
-
|
|
14595
|
-
|
|
14596
|
-
|
|
14597
|
-
|
|
14598
|
-
|
|
14599
|
-
|
|
14600
|
-
|
|
14601
|
-
|
|
14602
|
-
|
|
14603
|
-
|
|
14604
|
-
|
|
14605
|
-
|
|
15773
|
+
return result;
|
|
15774
|
+
};
|
|
15775
|
+
const redactFetchOutputs = (messages, protectedMessageCount = 10) => {
|
|
15776
|
+
const result = cloneMessages(messages);
|
|
15777
|
+
const protectedStart = getProtectedStartIndex(result, protectedMessageCount);
|
|
15778
|
+
for (let i = 0; i < protectedStart; i++) {
|
|
15779
|
+
const msg = result[i];
|
|
15780
|
+
if (msg.role !== "tool") {
|
|
15781
|
+
continue;
|
|
15782
|
+
}
|
|
15783
|
+
for (let j = 0; j < msg.content.length; j++) {
|
|
15784
|
+
const part = msg.content[j];
|
|
15785
|
+
if (part.type !== "tool-result") {
|
|
15786
|
+
continue;
|
|
15787
|
+
}
|
|
15788
|
+
const [serverName, toolName] = extractServerNameToolName(part.toolName);
|
|
15789
|
+
if (!isPowerTool(serverName) || toolName !== POWER_TOOL_FETCH) {
|
|
15790
|
+
continue;
|
|
15791
|
+
}
|
|
15792
|
+
const outputText = getToolOutputText(part.output);
|
|
15793
|
+
if (outputText.length > 0) {
|
|
15794
|
+
part.output = {
|
|
15795
|
+
type: "text",
|
|
15796
|
+
value: "<content redacted due to compaction, fetch again if content is needed>"
|
|
15797
|
+
};
|
|
14606
15798
|
}
|
|
14607
15799
|
}
|
|
14608
15800
|
}
|
|
14609
|
-
|
|
14610
|
-
|
|
14611
|
-
await this.pruneDeleted(projectDir);
|
|
14612
|
-
}
|
|
14613
|
-
}
|
|
15801
|
+
return result;
|
|
15802
|
+
};
|
|
14614
15803
|
const INTERNAL_TASK_ID = "internal";
|
|
14615
15804
|
const RESPONSE_CHUNK_FLUSH_INTERVAL_MS = 10;
|
|
14616
15805
|
const EMPTY_TASK_DATA = {
|
|
@@ -14622,7 +15811,6 @@ const EMPTY_TASK_DATA = {
|
|
|
14622
15811
|
agentTotalCost: 0,
|
|
14623
15812
|
mainModel: "",
|
|
14624
15813
|
currentMode: "agent",
|
|
14625
|
-
contextCompactingThreshold: 0,
|
|
14626
15814
|
weakModelLocked: false,
|
|
14627
15815
|
parentId: null,
|
|
14628
15816
|
lastAgentProviderMetadata: void 0
|
|
@@ -15117,7 +16305,7 @@ class Task {
|
|
|
15117
16305
|
isPromptRunning() {
|
|
15118
16306
|
return !!this.currentPromptContext || this.agent.isRunning() || this.isCompacting;
|
|
15119
16307
|
}
|
|
15120
|
-
async runPrompt(prompt, mode = this.task.currentMode || "agent", addToInputHistory = true, userMessageId = uuid.v4(), sendNotification = true) {
|
|
16308
|
+
async runPrompt(prompt, mode = this.task.currentMode || "agent", addToInputHistory = true, userMessageId = uuid.v4(), sendNotification = true, images) {
|
|
15121
16309
|
if (this.currentQuestion) {
|
|
15122
16310
|
if (await this.answerQuestion("n", prompt)) {
|
|
15123
16311
|
logger.debug("Processed by the answerQuestion function.");
|
|
@@ -15155,7 +16343,7 @@ class Task {
|
|
|
15155
16343
|
if (addToInputHistory) {
|
|
15156
16344
|
await this.project.addToInputHistory(prompt);
|
|
15157
16345
|
}
|
|
15158
|
-
this.addUserMessage(userMessageId, prompt);
|
|
16346
|
+
this.addUserMessage(userMessageId, prompt, promptContext, images);
|
|
15159
16347
|
this.addLogMessage("loading");
|
|
15160
16348
|
this.telemetryManager.captureRunPrompt(mode);
|
|
15161
16349
|
let responses = [];
|
|
@@ -15165,7 +16353,7 @@ class Task {
|
|
|
15165
16353
|
if (!profile) {
|
|
15166
16354
|
throw new Error("No active Agent profile found");
|
|
15167
16355
|
}
|
|
15168
|
-
responses = await this.runPromptInAgent(profile, mode, prompt, promptContext, void 0, void 0, void 0, true, sendNotification);
|
|
16356
|
+
responses = await this.runPromptInAgent(profile, mode, prompt, promptContext, void 0, void 0, void 0, true, sendNotification, images);
|
|
15169
16357
|
} else {
|
|
15170
16358
|
responses = await this.runPromptInAider(mode, prompt, promptContext, sendNotification);
|
|
15171
16359
|
}
|
|
@@ -15277,7 +16465,7 @@ class Task {
|
|
|
15277
16465
|
}
|
|
15278
16466
|
return responses;
|
|
15279
16467
|
}
|
|
15280
|
-
async runPromptInAgent(profile, mode, prompt, promptContext = { id: uuid.v4() }, contextMessages, contextFiles, systemPrompt, waitForCurrentAgentToFinish = true, sendNotification = true) {
|
|
16468
|
+
async runPromptInAgent(profile, mode, prompt, promptContext = { id: uuid.v4() }, contextMessages, contextFiles, systemPrompt, waitForCurrentAgentToFinish = true, sendNotification = true, images) {
|
|
15281
16469
|
if (waitForCurrentAgentToFinish) {
|
|
15282
16470
|
await this.waitForCurrentAgentToFinish();
|
|
15283
16471
|
}
|
|
@@ -15288,7 +16476,19 @@ class Task {
|
|
|
15288
16476
|
provider: this.task.provider || profile.provider,
|
|
15289
16477
|
model: this.task.model || profile.model
|
|
15290
16478
|
});
|
|
15291
|
-
const agentMessages = await this.agent.runAgent(
|
|
16479
|
+
const agentMessages = await this.agent.runAgent(
|
|
16480
|
+
this,
|
|
16481
|
+
profile,
|
|
16482
|
+
prompt,
|
|
16483
|
+
mode,
|
|
16484
|
+
promptContext,
|
|
16485
|
+
contextMessages,
|
|
16486
|
+
contextFiles,
|
|
16487
|
+
systemPrompt,
|
|
16488
|
+
true,
|
|
16489
|
+
void 0,
|
|
16490
|
+
images
|
|
16491
|
+
);
|
|
15292
16492
|
if (agentMessages.length > 0) {
|
|
15293
16493
|
this.contextManager.toConnectorMessages(agentMessages).forEach((message) => {
|
|
15294
16494
|
this.sendAddMessage(message.role, message.content, false);
|
|
@@ -16325,7 +17525,8 @@ ${contentText}</agent-response>`;
|
|
|
16325
17525
|
sendUserMessage(data) {
|
|
16326
17526
|
logger.debug("Sending user message:", {
|
|
16327
17527
|
baseDir: this.project.baseDir,
|
|
16328
|
-
content: data.content
|
|
17528
|
+
content: data.content?.substring(0, 100),
|
|
17529
|
+
hasImages: (data.images?.length ?? 0) > 0
|
|
16329
17530
|
});
|
|
16330
17531
|
this.eventManager.sendUserMessage(data);
|
|
16331
17532
|
}
|
|
@@ -16342,18 +17543,20 @@ ${contentText}</agent-response>`;
|
|
|
16342
17543
|
});
|
|
16343
17544
|
this.eventManager.sendTool(data);
|
|
16344
17545
|
}
|
|
16345
|
-
async clearContext(addToHistory = false, updateContextInfo = true) {
|
|
17546
|
+
async clearContext(addToHistory = false, updateContextInfo = true, updateTaskState = true, createSnapshot = true) {
|
|
16346
17547
|
logger.info("Clearing context:", {
|
|
16347
17548
|
baseDir: this.project.baseDir,
|
|
16348
17549
|
addToHistory,
|
|
16349
17550
|
updateContextInfo
|
|
16350
17551
|
});
|
|
16351
|
-
|
|
16352
|
-
|
|
16353
|
-
|
|
16354
|
-
|
|
16355
|
-
|
|
16356
|
-
|
|
17552
|
+
if (updateTaskState) {
|
|
17553
|
+
await this.updateTask({
|
|
17554
|
+
state: DefaultTaskState.Todo,
|
|
17555
|
+
metadata: void 0,
|
|
17556
|
+
lastAgentProviderMetadata: null
|
|
17557
|
+
});
|
|
17558
|
+
}
|
|
17559
|
+
this.contextManager.clearMessages(true, createSnapshot);
|
|
16357
17560
|
await this.runCommand("clear", addToHistory);
|
|
16358
17561
|
this.eventManager.sendClearTask(this.project.baseDir, this.taskId, true, false);
|
|
16359
17562
|
if (updateContextInfo) {
|
|
@@ -16369,6 +17572,29 @@ ${contentText}</agent-response>`;
|
|
|
16369
17572
|
this.contextManager.clearMessages();
|
|
16370
17573
|
await this.contextManager.save();
|
|
16371
17574
|
}
|
|
17575
|
+
sendContextInfoUpdated() {
|
|
17576
|
+
this.eventManager.sendContextInfoUpdated({
|
|
17577
|
+
baseDir: this.project.baseDir,
|
|
17578
|
+
taskId: this.taskId,
|
|
17579
|
+
canUndoContextChange: this.contextManager.hasUndoSnapshot()
|
|
17580
|
+
});
|
|
17581
|
+
}
|
|
17582
|
+
async undoContextChange() {
|
|
17583
|
+
const snapshot = this.contextManager.undoContextChange();
|
|
17584
|
+
if (!snapshot) {
|
|
17585
|
+
logger.debug("No undo snapshot available", { taskId: this.taskId });
|
|
17586
|
+
return false;
|
|
17587
|
+
}
|
|
17588
|
+
logger.info("Undoing context change", {
|
|
17589
|
+
baseDir: this.project.baseDir,
|
|
17590
|
+
taskId: this.taskId,
|
|
17591
|
+
messagesCount: snapshot.length
|
|
17592
|
+
});
|
|
17593
|
+
await this.contextManager.loadMessages(snapshot);
|
|
17594
|
+
await this.updateContextInfo();
|
|
17595
|
+
this.sendContextInfoUpdated();
|
|
17596
|
+
return true;
|
|
17597
|
+
}
|
|
16372
17598
|
/**
|
|
16373
17599
|
* Load context messages into the task context and send them to the UI.
|
|
16374
17600
|
* This is used for loading pre-authored context (e.g., from extensions).
|
|
@@ -16382,6 +17608,12 @@ ${contentText}</agent-response>`;
|
|
|
16382
17608
|
await this.contextManager.loadMessages(messages);
|
|
16383
17609
|
}
|
|
16384
17610
|
async interruptResponse(interruptId) {
|
|
17611
|
+
const extensionResult = await this.extensionManager.dispatchEvent("onInterrupted", { interruptId }, this.project, this);
|
|
17612
|
+
if (extensionResult.blocked) {
|
|
17613
|
+
logger.debug("Interrupt blocked by extension", { interruptId });
|
|
17614
|
+
return;
|
|
17615
|
+
}
|
|
17616
|
+
interruptId = extensionResult.interruptId;
|
|
16385
17617
|
if (interruptId) {
|
|
16386
17618
|
const subagentAbortController = this.subagentAbortControllers[interruptId];
|
|
16387
17619
|
if (subagentAbortController) {
|
|
@@ -16554,7 +17786,7 @@ ${contentText}</agent-response>`;
|
|
|
16554
17786
|
this.eventManager.sendTaskUpdated(this.task);
|
|
16555
17787
|
}
|
|
16556
17788
|
}
|
|
16557
|
-
addUserMessage(id, content, promptContext) {
|
|
17789
|
+
addUserMessage(id, content, promptContext, images) {
|
|
16558
17790
|
logger.info("Adding user message:", {
|
|
16559
17791
|
baseDir: this.project.baseDir,
|
|
16560
17792
|
content: content.substring(0, 100)
|
|
@@ -16565,6 +17797,7 @@ ${contentText}</agent-response>`;
|
|
|
16565
17797
|
baseDir: this.project.baseDir,
|
|
16566
17798
|
taskId: this.taskId,
|
|
16567
17799
|
content,
|
|
17800
|
+
images,
|
|
16568
17801
|
promptContext
|
|
16569
17802
|
};
|
|
16570
17803
|
this.eventManager.sendUserMessage(data);
|
|
@@ -16597,12 +17830,13 @@ ${contentText}</agent-response>`;
|
|
|
16597
17830
|
this.eventManager.sendTaskMessageRemoved(this.project.baseDir, this.taskId, messageIds);
|
|
16598
17831
|
}
|
|
16599
17832
|
}
|
|
16600
|
-
async redoUserPrompt(messageId, mode, updatedPrompt) {
|
|
17833
|
+
async redoUserPrompt(messageId, mode, updatedPrompt, updatedImages) {
|
|
16601
17834
|
logger.info("Redoing user prompt:", {
|
|
16602
17835
|
baseDir: this.project.baseDir,
|
|
16603
17836
|
messageId,
|
|
16604
17837
|
mode,
|
|
16605
|
-
hasUpdatedPrompt: !!updatedPrompt
|
|
17838
|
+
hasUpdatedPrompt: !!updatedPrompt,
|
|
17839
|
+
hasUpdatedImages: !!updatedImages
|
|
16606
17840
|
});
|
|
16607
17841
|
const removedMessages = this.contextManager.removeMessagesUpToUserMessage(messageId);
|
|
16608
17842
|
const originalUserMessage = removedMessages[0];
|
|
@@ -16610,14 +17844,16 @@ ${contentText}</agent-response>`;
|
|
|
16610
17844
|
logger.warn("Could not find the specified user message to redo.", { messageId });
|
|
16611
17845
|
return;
|
|
16612
17846
|
}
|
|
16613
|
-
const
|
|
17847
|
+
const originalText = extractTextContent(originalUserMessage.content);
|
|
17848
|
+
const promptToRun = updatedPrompt ?? originalText;
|
|
17849
|
+
const imagesToRun = updatedImages ?? (updatedPrompt !== void 0 ? void 0 : extractImagesFromContent(originalUserMessage.content));
|
|
16614
17850
|
if (promptToRun) {
|
|
16615
17851
|
logger.info("Found message content to run, reloading and re-running prompt.", {
|
|
16616
17852
|
remainingMessagesCount: (await this.contextManager.getContextMessages()).length
|
|
16617
17853
|
});
|
|
16618
17854
|
this.sendTaskMessageRemoved(removedMessages.slice(1).map((msg) => msg.id));
|
|
16619
17855
|
await this.updateContextInfo();
|
|
16620
|
-
void this.runPrompt(promptToRun, mode, false, originalUserMessage.id);
|
|
17856
|
+
void this.runPrompt(promptToRun, mode, false, originalUserMessage.id, true, imagesToRun);
|
|
16621
17857
|
} else {
|
|
16622
17858
|
logger.warn("Could not find a previous user message to redo or an updated prompt to run.");
|
|
16623
17859
|
}
|
|
@@ -16713,6 +17949,22 @@ ${contentText}</agent-response>`;
|
|
|
16713
17949
|
}
|
|
16714
17950
|
return skillMessages;
|
|
16715
17951
|
}
|
|
17952
|
+
async smartCompactConversation(contextMessages, infoMessage = "Conversation smart-compacted.") {
|
|
17953
|
+
if (!contextMessages) {
|
|
17954
|
+
contextMessages = await this.contextManager.getContextMessages();
|
|
17955
|
+
}
|
|
17956
|
+
if (contextMessages.length === 0) {
|
|
17957
|
+
this.addLogMessage("warning", "No conversation to compact.");
|
|
17958
|
+
return [];
|
|
17959
|
+
}
|
|
17960
|
+
await this.contextManager.backupContext();
|
|
17961
|
+
const compactedMessages = smartCompactMessages(contextMessages);
|
|
17962
|
+
this.contextManager.setContextMessages(compactedMessages);
|
|
17963
|
+
await this.contextManager.loadMessages(compactedMessages, false);
|
|
17964
|
+
await this.updateContextInfo();
|
|
17965
|
+
this.addLogMessage("info", infoMessage, false, void 0, ["undoContextChange"]);
|
|
17966
|
+
return compactedMessages;
|
|
17967
|
+
}
|
|
16716
17968
|
async compactConversation(mode, customInstructions, profile = null, contextMessages, promptContext, abortSignal, waitForAgentCompletion = true, loadingMessage = "Compacting conversation...") {
|
|
16717
17969
|
if (!profile) {
|
|
16718
17970
|
profile = await this.getTaskAgentProfile();
|
|
@@ -16801,7 +18053,7 @@ ${contentText}</agent-response>`;
|
|
|
16801
18053
|
await this.contextManager.loadMessages(await this.contextManager.getContextMessages());
|
|
16802
18054
|
}
|
|
16803
18055
|
await this.updateContextInfo();
|
|
16804
|
-
this.addLogMessage("info", "Conversation compacted.");
|
|
18056
|
+
this.addLogMessage("info", "Conversation compacted.", false, void 0, ["undoContextChange"]);
|
|
16805
18057
|
} catch (error) {
|
|
16806
18058
|
logger.error("Failed to compact conversation", {
|
|
16807
18059
|
baseDir: this.project.baseDir,
|
|
@@ -16899,6 +18151,7 @@ ${contentText}</agent-response>`;
|
|
|
16899
18151
|
}
|
|
16900
18152
|
async updateContextInfo(checkContextFilesIncluded = false, checkRepoMapIncluded = false) {
|
|
16901
18153
|
void this.debouncedUpdateContextInfo(checkContextFilesIncluded, checkRepoMapIncluded);
|
|
18154
|
+
void this.sendSkillsUpdated();
|
|
16902
18155
|
}
|
|
16903
18156
|
debouncedUpdateContextInfo = debounce(async (checkContextFilesIncluded = false, checkRepoMapIncluded = false) => {
|
|
16904
18157
|
void this.sendRequestContextInfo();
|
|
@@ -17172,7 +18425,6 @@ ${error.stderr}`,
|
|
|
17172
18425
|
id: uuid.v4()
|
|
17173
18426
|
};
|
|
17174
18427
|
this.addUserMessage(promptContext.id, prompt);
|
|
17175
|
-
this.addLogMessage("loading", "Executing custom command...");
|
|
17176
18428
|
try {
|
|
17177
18429
|
if (!AIDER_MODES.includes(mode)) {
|
|
17178
18430
|
const profile = await this.getTaskAgentProfile();
|
|
@@ -17180,11 +18432,22 @@ ${error.stderr}`,
|
|
|
17180
18432
|
this.addLogMessage("error", "No active Agent profile found");
|
|
17181
18433
|
return;
|
|
17182
18434
|
}
|
|
18435
|
+
if (command.skills?.length) {
|
|
18436
|
+
for (const skillName of command.skills) {
|
|
18437
|
+
try {
|
|
18438
|
+
await this.activateSkill(skillName);
|
|
18439
|
+
} catch (error) {
|
|
18440
|
+
logger.warn(`Failed to activate skill '${skillName}' for command '${commandName}': ${error instanceof Error ? error.message : String(error)}`);
|
|
18441
|
+
}
|
|
18442
|
+
}
|
|
18443
|
+
}
|
|
17183
18444
|
const systemPrompt = await this.promptsManager.getSystemPrompt(this.store.getSettings(), this, profile, command.autoApprove ?? this.task.autoApprove);
|
|
17184
18445
|
const messages = command.includeContext === false ? [] : void 0;
|
|
17185
18446
|
const contextFiles = command.includeContext === false ? [] : void 0;
|
|
18447
|
+
this.addLogMessage("loading", "Executing custom command...");
|
|
17186
18448
|
await this.runPromptInAgent(profile, mode, prompt, promptContext, messages, contextFiles, systemPrompt);
|
|
17187
18449
|
} else {
|
|
18450
|
+
this.addLogMessage("loading", "Executing custom command...");
|
|
17188
18451
|
await this.runPromptInAider(mode, prompt, promptContext);
|
|
17189
18452
|
}
|
|
17190
18453
|
} finally {
|
|
@@ -18106,6 +19369,9 @@ class Project {
|
|
|
18106
19369
|
if (!parentTask) {
|
|
18107
19370
|
throw new Error(`Parent task with id ${normalizedParams.parentId} not found`);
|
|
18108
19371
|
}
|
|
19372
|
+
if (parentTask.task.parentId) {
|
|
19373
|
+
normalizedParams.parentId = parentTask.task.parentId;
|
|
19374
|
+
}
|
|
18109
19375
|
if (!parentTask.task.createdAt) {
|
|
18110
19376
|
await parentTask.saveTask();
|
|
18111
19377
|
}
|
|
@@ -18143,9 +19409,7 @@ class Project {
|
|
|
18143
19409
|
};
|
|
18144
19410
|
}
|
|
18145
19411
|
const projectSettings = this.getProjectSettings();
|
|
18146
|
-
const taskSettings = this.store.getSettings().taskSettings;
|
|
18147
19412
|
const taskData = {
|
|
18148
|
-
contextCompactingThreshold: taskSettings.contextCompactingThreshold,
|
|
18149
19413
|
autoApprove: projectSettings.autoApproveLocked ? true : initialTaskData.autoApprove,
|
|
18150
19414
|
...initialTaskData
|
|
18151
19415
|
};
|
|
@@ -18164,7 +19428,14 @@ class Project {
|
|
|
18164
19428
|
}
|
|
18165
19429
|
return task.task;
|
|
18166
19430
|
}
|
|
18167
|
-
|
|
19431
|
+
generateShortTaskId() {
|
|
19432
|
+
let id;
|
|
19433
|
+
do {
|
|
19434
|
+
id = uuid.v4().substring(0, 8);
|
|
19435
|
+
} while (this.tasks.has(id));
|
|
19436
|
+
return id;
|
|
19437
|
+
}
|
|
19438
|
+
async prepareTask(taskId = this.generateShortTaskId(), initialTaskData) {
|
|
18168
19439
|
const task = new Task(
|
|
18169
19440
|
this,
|
|
18170
19441
|
taskId,
|
|
@@ -18333,6 +19604,9 @@ class Project {
|
|
|
18333
19604
|
getAgentProfiles() {
|
|
18334
19605
|
return this.agentProfileManager.getProjectProfiles(this);
|
|
18335
19606
|
}
|
|
19607
|
+
resolveAgentProfile(id) {
|
|
19608
|
+
return this.agentProfileManager.resolveAgentProfile(id);
|
|
19609
|
+
}
|
|
18336
19610
|
/**
|
|
18337
19611
|
* Checks if any other task (excluding the specified taskId) uses the given worktree path.
|
|
18338
19612
|
*/
|
|
@@ -18588,6 +19862,10 @@ class EventManager {
|
|
|
18588
19862
|
this.sendToWindows("clear-task", data);
|
|
18589
19863
|
this.broadcastToEventConnectors("clear-task", data);
|
|
18590
19864
|
}
|
|
19865
|
+
sendContextInfoUpdated(data) {
|
|
19866
|
+
this.sendToWindows("context-info-updated", data);
|
|
19867
|
+
this.broadcastToEventConnectors("context-info-updated", data);
|
|
19868
|
+
}
|
|
18591
19869
|
// File management events
|
|
18592
19870
|
sendFileAdded(baseDir, taskId, file) {
|
|
18593
19871
|
const data = {
|
|
@@ -20979,7 +22257,8 @@ const createLmStudioLlm = (profile, model, settings, projectDir) => {
|
|
|
20979
22257
|
const lmStudioProvider = openaiCompatible.createOpenAICompatible({
|
|
20980
22258
|
name: "lmstudio",
|
|
20981
22259
|
baseURL: baseUrl,
|
|
20982
|
-
headers: profile.headers
|
|
22260
|
+
headers: profile.headers,
|
|
22261
|
+
includeUsage: true
|
|
20983
22262
|
});
|
|
20984
22263
|
return lmStudioProvider(model.id);
|
|
20985
22264
|
};
|
|
@@ -21652,12 +22931,18 @@ const loadOpenaiCompatibleModels = async (profile, settings) => {
|
|
|
21652
22931
|
return { models: [], success: false, error: errorMsg };
|
|
21653
22932
|
}
|
|
21654
22933
|
const data = await response.json();
|
|
21655
|
-
const models = data.data?.map(
|
|
21656
|
-
|
|
21657
|
-
|
|
21658
|
-
|
|
21659
|
-
|
|
21660
|
-
|
|
22934
|
+
const models = data.data?.map(
|
|
22935
|
+
(model) => {
|
|
22936
|
+
const maxInputTokens = model.max_model_len ?? model.context_length ?? model.num_ctx ?? model.context_window;
|
|
22937
|
+
const maxOutputTokensLimit = model.max_completion_tokens ?? model.max_tokens;
|
|
22938
|
+
return {
|
|
22939
|
+
id: model.id,
|
|
22940
|
+
providerId: profile.id,
|
|
22941
|
+
...maxInputTokens != null && { maxInputTokens },
|
|
22942
|
+
...maxOutputTokensLimit != null && { maxOutputTokensLimit }
|
|
22943
|
+
};
|
|
22944
|
+
}
|
|
22945
|
+
) || [];
|
|
21661
22946
|
logger.info(`Loaded ${models.length} OpenAI-compatible models for profile ${profile.id}`);
|
|
21662
22947
|
return { models, success: true };
|
|
21663
22948
|
} catch (error) {
|
|
@@ -21706,11 +22991,14 @@ const createOpenAiCompatibleLlm = (profile, model, settings, projectDir) => {
|
|
|
21706
22991
|
if (!baseUrl) {
|
|
21707
22992
|
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
22993
|
}
|
|
22994
|
+
const providerOverrides = model.providerOverrides;
|
|
22995
|
+
const trackTokenUsage = providerOverrides?.trackTokenUsage ?? provider.trackTokenUsage;
|
|
21709
22996
|
const compatibleProvider = openaiCompatible.createOpenAICompatible({
|
|
21710
22997
|
name: provider.name,
|
|
21711
22998
|
apiKey,
|
|
21712
22999
|
baseURL: baseUrl,
|
|
21713
|
-
headers: profile.headers
|
|
23000
|
+
headers: profile.headers,
|
|
23001
|
+
includeUsage: trackTokenUsage !== false
|
|
21714
23002
|
});
|
|
21715
23003
|
return compatibleProvider(model.id);
|
|
21716
23004
|
};
|
|
@@ -22192,7 +23480,7 @@ const getRequestyUsageReport = (task, provider, model, usage, providerMetadata)
|
|
|
22192
23480
|
const cacheWriteTokens = requesty?.usage?.cachingTokens ?? 0;
|
|
22193
23481
|
const cacheReadTokens = requesty?.usage?.cachedTokens ?? 0;
|
|
22194
23482
|
const sentTokens = totalSentTokens - cacheReadTokens;
|
|
22195
|
-
const messageCost = calculateRequestyCost(model, sentTokens, receivedTokens, cacheWriteTokens, cacheReadTokens);
|
|
23483
|
+
const messageCost = requesty?.usage?.cost ?? calculateRequestyCost(model, sentTokens, receivedTokens, cacheWriteTokens, cacheReadTokens);
|
|
22196
23484
|
return {
|
|
22197
23485
|
model: `${provider.id}/${model.id}`,
|
|
22198
23486
|
sentTokens,
|
|
@@ -23102,6 +24390,7 @@ class ModelManager {
|
|
|
23102
24390
|
getModelSettings(providerId, modelId, useModelInfoFallback = false) {
|
|
23103
24391
|
let model;
|
|
23104
24392
|
const providerModels = this.providerModels[providerId];
|
|
24393
|
+
logger.debug(`getModelSettings providerModels for provider: ${providerId}`, { providerModels });
|
|
23105
24394
|
if (providerModels) {
|
|
23106
24395
|
model = providerModels.find((m) => m.id === modelId);
|
|
23107
24396
|
}
|
|
@@ -23115,6 +24404,11 @@ class ModelManager {
|
|
|
23115
24404
|
};
|
|
23116
24405
|
}
|
|
23117
24406
|
}
|
|
24407
|
+
logger.debug("getModelSettings model", {
|
|
24408
|
+
providerId,
|
|
24409
|
+
modelId,
|
|
24410
|
+
model
|
|
24411
|
+
});
|
|
23118
24412
|
return model;
|
|
23119
24413
|
}
|
|
23120
24414
|
createLlm(provider, model, settings, projectDir, toolSet, systemPrompt, providerMetadata) {
|
|
@@ -23680,7 +24974,7 @@ class VersionsManager {
|
|
|
23680
24974
|
...this.versionsInfo
|
|
23681
24975
|
};
|
|
23682
24976
|
}
|
|
23683
|
-
logger.
|
|
24977
|
+
logger.debug("Checking for version updates...");
|
|
23684
24978
|
const aiderDeskCurrentVersion = app.getVersion();
|
|
23685
24979
|
const autoUpdater = require("electron-updater").autoUpdater;
|
|
23686
24980
|
if (this.pythonInstaller) {
|
|
@@ -25036,6 +26330,41 @@ class ExtensionFetcher {
|
|
|
25036
26330
|
const extensions = [];
|
|
25037
26331
|
try {
|
|
25038
26332
|
const entries = await fs.readdir(extensionsPath, { withFileTypes: true });
|
|
26333
|
+
const rootIndexTs = path.join(extensionsPath, "index.ts");
|
|
26334
|
+
const rootIndexJs = path.join(extensionsPath, "index.js");
|
|
26335
|
+
const rootPackageJson = path.join(extensionsPath, "package.json");
|
|
26336
|
+
let rootIndexFile = null;
|
|
26337
|
+
if (await this.fileExists(rootIndexTs)) {
|
|
26338
|
+
rootIndexFile = rootIndexTs;
|
|
26339
|
+
} else if (await this.fileExists(rootIndexJs)) {
|
|
26340
|
+
rootIndexFile = rootIndexJs;
|
|
26341
|
+
}
|
|
26342
|
+
const hasRootPackageJson = await this.fileExists(rootPackageJson);
|
|
26343
|
+
if (rootIndexFile && hasRootPackageJson) {
|
|
26344
|
+
const metadata = await this.extractMetadataFromLocalFile(rootIndexFile);
|
|
26345
|
+
if (metadata) {
|
|
26346
|
+
const repoName = this.getRepoName(repoUrl);
|
|
26347
|
+
let readmeContent;
|
|
26348
|
+
const readmePath = path.join(extensionsPath, "README.md");
|
|
26349
|
+
try {
|
|
26350
|
+
const content = await fs.readFile(readmePath, "utf-8");
|
|
26351
|
+
if (content.trim()) {
|
|
26352
|
+
readmeContent = content;
|
|
26353
|
+
}
|
|
26354
|
+
} catch {
|
|
26355
|
+
}
|
|
26356
|
+
extensions.push({
|
|
26357
|
+
...metadata,
|
|
26358
|
+
id: repoName,
|
|
26359
|
+
type: "folder",
|
|
26360
|
+
folder: repoName,
|
|
26361
|
+
repositoryUrl: repoUrl,
|
|
26362
|
+
hasDependencies: true,
|
|
26363
|
+
readmeContent
|
|
26364
|
+
});
|
|
26365
|
+
return extensions;
|
|
26366
|
+
}
|
|
26367
|
+
}
|
|
25039
26368
|
for (const entry of entries) {
|
|
25040
26369
|
if (entry.isDirectory()) {
|
|
25041
26370
|
const indexPathTs = path.join(extensionsPath, entry.name, "index.ts");
|
|
@@ -25089,6 +26418,17 @@ class ExtensionFetcher {
|
|
|
25089
26418
|
}
|
|
25090
26419
|
return extensions;
|
|
25091
26420
|
}
|
|
26421
|
+
getRepoName(repoUrl) {
|
|
26422
|
+
try {
|
|
26423
|
+
const url = new URL(repoUrl);
|
|
26424
|
+
const pathParts = url.pathname.split("/").filter(Boolean);
|
|
26425
|
+
if (pathParts.length >= 2) {
|
|
26426
|
+
return pathParts[1].replace(/\.git$/, "");
|
|
26427
|
+
}
|
|
26428
|
+
} catch {
|
|
26429
|
+
}
|
|
26430
|
+
return repoUrl.replace(/[^a-zA-Z0-9]/g, "-");
|
|
26431
|
+
}
|
|
25092
26432
|
async fileExists(filePath) {
|
|
25093
26433
|
try {
|
|
25094
26434
|
await fs.access(filePath);
|
|
@@ -26009,7 +27349,7 @@ class ExtensionManager {
|
|
|
26009
27349
|
}
|
|
26010
27350
|
collectedSkills.push({
|
|
26011
27351
|
...skill,
|
|
26012
|
-
location: "extension"
|
|
27352
|
+
location: skill.location || "extension"
|
|
26013
27353
|
});
|
|
26014
27354
|
}
|
|
26015
27355
|
} catch (error) {
|
|
@@ -26350,12 +27690,19 @@ class ExtensionManager {
|
|
|
26350
27690
|
} else if (extension.type === "folder" && extension.folder) {
|
|
26351
27691
|
const repoDir = await this.fetcher.ensureRepoCloned(repositoryUrl);
|
|
26352
27692
|
const extensionsPath = this.fetcher.getExtensionsPath(repositoryUrl, repoDir);
|
|
26353
|
-
const sourcePath = path.join(extensionsPath, extension.folder);
|
|
26354
27693
|
const targetPath = path.join(targetDir, extension.folder);
|
|
27694
|
+
let sourcePath = path.join(extensionsPath, extension.folder);
|
|
27695
|
+
if (!await this.fileExists(sourcePath)) {
|
|
27696
|
+
sourcePath = extensionsPath;
|
|
27697
|
+
}
|
|
26355
27698
|
if (!await this.fileExists(sourcePath)) {
|
|
26356
27699
|
throw new Error(`Extension folder not found in repository: ${extension.folder}`);
|
|
26357
27700
|
}
|
|
26358
27701
|
await fs.cp(sourcePath, targetPath, { recursive: true });
|
|
27702
|
+
const gitDir = path.join(targetPath, ".git");
|
|
27703
|
+
if (await this.fileExists(gitDir)) {
|
|
27704
|
+
await fs.rm(gitDir, { recursive: true, force: true });
|
|
27705
|
+
}
|
|
26359
27706
|
if (extension.hasDependencies) {
|
|
26360
27707
|
logger.debug(`[Extensions] Installing dependencies for ${extension.name}...`);
|
|
26361
27708
|
await this.installDependencies(targetPath);
|
|
@@ -26480,12 +27827,19 @@ class ExtensionManager {
|
|
|
26480
27827
|
} else if (extension.type === "folder" && extension.folder) {
|
|
26481
27828
|
const repoDir = await this.fetcher.ensureRepoCloned(repositoryUrl);
|
|
26482
27829
|
const extensionsPath = this.fetcher.getExtensionsPath(repositoryUrl, repoDir);
|
|
26483
|
-
const sourcePath = path.join(extensionsPath, extension.folder);
|
|
26484
27830
|
const targetPath = path.join(targetDir, extension.folder);
|
|
27831
|
+
let sourcePath = path.join(extensionsPath, extension.folder);
|
|
27832
|
+
if (!await this.fileExists(sourcePath)) {
|
|
27833
|
+
sourcePath = extensionsPath;
|
|
27834
|
+
}
|
|
26485
27835
|
if (!await this.fileExists(sourcePath)) {
|
|
26486
27836
|
throw new Error(`Extension folder not found in repository: ${extension.folder}`);
|
|
26487
27837
|
}
|
|
26488
27838
|
await fs.cp(sourcePath, targetPath, { recursive: true });
|
|
27839
|
+
const gitDir = path.join(targetPath, ".git");
|
|
27840
|
+
if (await this.fileExists(gitDir)) {
|
|
27841
|
+
await fs.rm(gitDir, { recursive: true, force: true });
|
|
27842
|
+
}
|
|
26489
27843
|
const packageJsonPath = path.join(targetPath, "package.json");
|
|
26490
27844
|
if (await this.fileExists(packageJsonPath)) {
|
|
26491
27845
|
logger.debug(`[Extensions] Installing dependencies for ${extension.name}...`);
|
|
@@ -26535,7 +27889,7 @@ class ExtensionManager {
|
|
|
26535
27889
|
if (result && typeof result === "object") {
|
|
26536
27890
|
const partialEvent = result;
|
|
26537
27891
|
currentEvent = { ...currentEvent, ...partialEvent };
|
|
26538
|
-
if ("blocked" in currentEvent && currentEvent.blocked
|
|
27892
|
+
if ("blocked" in currentEvent && currentEvent.blocked) {
|
|
26539
27893
|
logger.debug(`[Extensions] Event '${String(eventName)}' blocked by extension '${metadata.name}'`);
|
|
26540
27894
|
break;
|
|
26541
27895
|
}
|
|
@@ -26704,8 +28058,8 @@ class EventsHandler {
|
|
|
26704
28058
|
const removedIds = await this.projectManager.getProject(baseDir).getTask(taskId)?.removeMessagesUpTo(messageId) ?? [];
|
|
26705
28059
|
this.eventManager.sendTaskMessageRemoved(baseDir, taskId, removedIds);
|
|
26706
28060
|
}
|
|
26707
|
-
async redoUserPrompt(baseDir, taskId, messageId, mode, updatedPrompt) {
|
|
26708
|
-
void this.projectManager.getProject(baseDir).getTask(taskId)?.redoUserPrompt(messageId, mode, updatedPrompt);
|
|
28061
|
+
async redoUserPrompt(baseDir, taskId, messageId, mode, updatedPrompt, updatedImages) {
|
|
28062
|
+
void this.projectManager.getProject(baseDir).getTask(taskId)?.redoUserPrompt(messageId, mode, updatedPrompt, updatedImages);
|
|
26709
28063
|
}
|
|
26710
28064
|
async resumeTask(baseDir, taskId) {
|
|
26711
28065
|
void this.projectManager.getProject(baseDir).getTask(taskId)?.resumeTask();
|
|
@@ -26716,6 +28070,19 @@ class EventsHandler {
|
|
|
26716
28070
|
await task.compactConversation(mode, customInstructions);
|
|
26717
28071
|
}
|
|
26718
28072
|
}
|
|
28073
|
+
async smartCompactConversation(baseDir, taskId) {
|
|
28074
|
+
const task = this.projectManager.getProject(baseDir).getTask(taskId);
|
|
28075
|
+
if (task) {
|
|
28076
|
+
await task.smartCompactConversation();
|
|
28077
|
+
}
|
|
28078
|
+
}
|
|
28079
|
+
async undoContextChange(baseDir, taskId) {
|
|
28080
|
+
const task = this.projectManager.getProject(baseDir).getTask(taskId);
|
|
28081
|
+
if (task) {
|
|
28082
|
+
return task.undoContextChange();
|
|
28083
|
+
}
|
|
28084
|
+
return false;
|
|
28085
|
+
}
|
|
26719
28086
|
async handoffConversation(baseDir, taskId, focus) {
|
|
26720
28087
|
const task = this.projectManager.getProject(baseDir).getTask(taskId);
|
|
26721
28088
|
if (!task) {
|
|
@@ -26800,8 +28167,8 @@ class EventsHandler {
|
|
|
26800
28167
|
applyEdits(baseDir, taskId, edits) {
|
|
26801
28168
|
this.projectManager.getProject(baseDir).getTask(taskId)?.applyEdits(edits);
|
|
26802
28169
|
}
|
|
26803
|
-
async runPrompt(baseDir, taskId, prompt, mode) {
|
|
26804
|
-
return this.projectManager.getProject(baseDir).getTask(taskId)?.runPrompt(prompt, mode) || [];
|
|
28170
|
+
async runPrompt(baseDir, taskId, prompt, mode, images) {
|
|
28171
|
+
return this.projectManager.getProject(baseDir).getTask(taskId)?.runPrompt(prompt, mode, true, void 0, true, images) || [];
|
|
26805
28172
|
}
|
|
26806
28173
|
async savePrompt(baseDir, taskId, prompt) {
|
|
26807
28174
|
return this.projectManager.getProject(baseDir).getTask(taskId)?.savePromptOnly(prompt);
|
|
@@ -28760,6 +30127,16 @@ const migrateSettingsV18toV19 = (settings) => {
|
|
|
28760
30127
|
}
|
|
28761
30128
|
};
|
|
28762
30129
|
};
|
|
30130
|
+
const migrateSettingsV19toV20 = (settings) => {
|
|
30131
|
+
const oldThreshold = settings.taskSettings?.contextCompactingThreshold;
|
|
30132
|
+
return {
|
|
30133
|
+
...settings,
|
|
30134
|
+
taskSettings: {
|
|
30135
|
+
...settings.taskSettings,
|
|
30136
|
+
contextCompactingThreshold: typeof oldThreshold === "number" ? { percentage: oldThreshold, tokens: 1e5 } : oldThreshold || { percentage: 90, tokens: 1e5 }
|
|
30137
|
+
}
|
|
30138
|
+
};
|
|
30139
|
+
};
|
|
28763
30140
|
const DEFAULT_SETTINGS = {
|
|
28764
30141
|
language: "en",
|
|
28765
30142
|
startupMode: ProjectStartMode.Empty,
|
|
@@ -28820,7 +30197,7 @@ const DEFAULT_SETTINGS = {
|
|
|
28820
30197
|
autoGenerateTaskName: true,
|
|
28821
30198
|
showTaskStateActions: true,
|
|
28822
30199
|
worktreeSymlinkFolders: ["node_modules", "vendor", "__pycache__", ".venv", "venv"],
|
|
28823
|
-
contextCompactingThreshold:
|
|
30200
|
+
contextCompactingThreshold: { percentage: 90, tokens: 1e5 },
|
|
28824
30201
|
contextCompactionType: ContextCompactionType.Compact,
|
|
28825
30202
|
defaultWorkingMode: "local",
|
|
28826
30203
|
worktreeBranchPrefix: "aider-desk/task/",
|
|
@@ -28838,7 +30215,7 @@ const DEFAULT_SETTINGS = {
|
|
|
28838
30215
|
const compareBaseDirs = (baseDir1, baseDir2) => {
|
|
28839
30216
|
return normalizeBaseDir(baseDir1) === normalizeBaseDir(baseDir2);
|
|
28840
30217
|
};
|
|
28841
|
-
const CURRENT_SETTINGS_VERSION =
|
|
30218
|
+
const CURRENT_SETTINGS_VERSION = 20;
|
|
28842
30219
|
class Store {
|
|
28843
30220
|
// @ts-expect-error expected to be initialized
|
|
28844
30221
|
store;
|
|
@@ -28989,6 +30366,10 @@ class Store {
|
|
|
28989
30366
|
settings = migrateSettingsV18toV19(settings);
|
|
28990
30367
|
settingsVersion = 19;
|
|
28991
30368
|
}
|
|
30369
|
+
if (settingsVersion === 19) {
|
|
30370
|
+
settings = migrateSettingsV19toV20(settings);
|
|
30371
|
+
settingsVersion = 20;
|
|
30372
|
+
}
|
|
28992
30373
|
this.store.set("settings", settings);
|
|
28993
30374
|
this.store.set("openProjects", openProjects || []);
|
|
28994
30375
|
this.store.set("providers", providers || []);
|
|
@@ -29182,4 +30563,5 @@ if (!process.env.VITEST) {
|
|
|
29182
30563
|
}
|
|
29183
30564
|
exports.addProjectsFromEnv = addProjectsFromEnv;
|
|
29184
30565
|
exports.initializeLangfuseExporter = initializeLangfuseExporter;
|
|
30566
|
+
exports.initializePostHogExporter = initializePostHogExporter;
|
|
29185
30567
|
exports.logger = logger;
|