micro-models-agent 0.26.0 → 0.27.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/dist/main.js +1223 -885
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2391,7 +2391,7 @@ var init_en = __esm(() => {
|
|
|
2391
2391
|
"tool.interactive_disabled": "Interactive tool is disabled in exit-on-complete mode. Proceed without asking the user.",
|
|
2392
2392
|
"proc.started": `Started background process {id} (PID {pid}).
|
|
2393
2393
|
Command: {command}`,
|
|
2394
|
-
"proc.
|
|
2394
|
+
"proc.promoted_hint": "Command still running after {ms} ms — moved to the background",
|
|
2395
2395
|
"proc.manage_hint": "Check output: process_log id={id}. Stop it: process_kill id={id}. List all: process_list.",
|
|
2396
2396
|
"proc.none": "No background processes running.",
|
|
2397
2397
|
"proc.not_found": "Process not found: {id}",
|
|
@@ -2400,7 +2400,6 @@ Command: {command}`,
|
|
|
2400
2400
|
"proc.list_header": "Background processes",
|
|
2401
2401
|
"proc.log_header": "Process {id} ({status}) output:",
|
|
2402
2402
|
"proc.log_empty": "(no output yet)",
|
|
2403
|
-
"proc.timed_out": "Command timed out after {ms} ms and was killed.",
|
|
2404
2403
|
"proc.hint": "Manage them with {list}, {log}, {kill}.",
|
|
2405
2404
|
"proc.status_running": "running",
|
|
2406
2405
|
"proc.status_exited": "exited",
|
|
@@ -2717,7 +2716,7 @@ Use this knowledge to answer the user's question.`,
|
|
|
2717
2716
|
"hall.repetitive": "Response too repetitive ({pct}% overlap)",
|
|
2718
2717
|
"hall.uncertainty": "Uncertainty markers: {markers}",
|
|
2719
2718
|
"hall.unknown_paths": "Mentioned file paths not found in known files: {paths}",
|
|
2720
|
-
"hall.
|
|
2719
|
+
"hall.contradiction_llm": "Contradicts an earlier decision{reason}",
|
|
2721
2720
|
"hall.uncertainty_prefix": `
|
|
2722
2721
|
|
|
2723
2722
|
[⚠️ Uncertainty] `,
|
|
@@ -2936,7 +2935,7 @@ var init_ru = __esm(() => {
|
|
|
2936
2935
|
"tool.interactive_disabled": "Интерактивный инструмент отключён в режиме exit-on-complete. Продолжай без вопроса пользователю.",
|
|
2937
2936
|
"proc.started": `Фоновый процесс запущен: {id} (PID {pid}).
|
|
2938
2937
|
Команда: {command}`,
|
|
2939
|
-
"proc.
|
|
2938
|
+
"proc.promoted_hint": "Команда всё ещё выполняется через {ms} мс — переведена в фоновый режим",
|
|
2940
2939
|
"proc.manage_hint": "Проверить вывод: process_log id={id}. Остановить: process_kill id={id}. Список всех: process_list.",
|
|
2941
2940
|
"proc.none": "Фоновых процессов нет.",
|
|
2942
2941
|
"proc.not_found": "Процесс не найден: {id}",
|
|
@@ -2945,7 +2944,6 @@ var init_ru = __esm(() => {
|
|
|
2945
2944
|
"proc.list_header": "Фоновые процессы",
|
|
2946
2945
|
"proc.log_header": "Вывод процесса {id} ({status}):",
|
|
2947
2946
|
"proc.log_empty": "(вывода пока нет)",
|
|
2948
|
-
"proc.timed_out": "Команда превысила таймаут {ms} мс и была остановлена.",
|
|
2949
2947
|
"proc.hint": "Управление: {list}, {log}, {kill}.",
|
|
2950
2948
|
"proc.status_running": "работает",
|
|
2951
2949
|
"proc.status_exited": "завершён",
|
|
@@ -3262,7 +3260,7 @@ var init_ru = __esm(() => {
|
|
|
3262
3260
|
"hall.repetitive": "Слишком повторяющийся ответ ({pct}% совпадение)",
|
|
3263
3261
|
"hall.uncertainty": "Маркеры неопределённости: {markers}",
|
|
3264
3262
|
"hall.unknown_paths": "Упомянутые файлы не найдены: {paths}",
|
|
3265
|
-
"hall.
|
|
3263
|
+
"hall.contradiction_llm": "Противоречит ранее принятому решению{reason}",
|
|
3266
3264
|
"hall.uncertainty_prefix": `
|
|
3267
3265
|
|
|
3268
3266
|
[⚠️ Неопределённость] `,
|
|
@@ -4929,8 +4927,12 @@ class ToolRegistry {
|
|
|
4929
4927
|
}
|
|
4930
4928
|
|
|
4931
4929
|
// src/modules/processes/runner.ts
|
|
4932
|
-
|
|
4933
|
-
|
|
4930
|
+
function registerKillable(callId, kill) {
|
|
4931
|
+
activeChildren.set(callId, { kill });
|
|
4932
|
+
}
|
|
4933
|
+
function unregisterKillable(callId) {
|
|
4934
|
+
activeChildren.delete(callId);
|
|
4935
|
+
}
|
|
4934
4936
|
function killByCallId(callId) {
|
|
4935
4937
|
const entry = activeChildren.get(callId);
|
|
4936
4938
|
if (!entry)
|
|
@@ -4941,95 +4943,6 @@ function killByCallId(callId) {
|
|
|
4941
4943
|
} catch {}
|
|
4942
4944
|
return true;
|
|
4943
4945
|
}
|
|
4944
|
-
function killTree(child) {
|
|
4945
|
-
const pid = child.pid;
|
|
4946
|
-
if (!pid)
|
|
4947
|
-
return;
|
|
4948
|
-
if (platform() === "win32") {
|
|
4949
|
-
spawn("taskkill", ["/pid", String(pid), "/T", "/F"], {
|
|
4950
|
-
windowsHide: true,
|
|
4951
|
-
stdio: "ignore"
|
|
4952
|
-
});
|
|
4953
|
-
return;
|
|
4954
|
-
}
|
|
4955
|
-
try {
|
|
4956
|
-
process.kill(-pid, "SIGTERM");
|
|
4957
|
-
} catch {
|
|
4958
|
-
try {
|
|
4959
|
-
child.kill("SIGKILL");
|
|
4960
|
-
} catch {}
|
|
4961
|
-
}
|
|
4962
|
-
}
|
|
4963
|
-
function runCommand(command, options = {}) {
|
|
4964
|
-
const {
|
|
4965
|
-
cwd,
|
|
4966
|
-
callId,
|
|
4967
|
-
timeoutMs = 120000,
|
|
4968
|
-
maxBuffer = 10 * 1024 * 1024
|
|
4969
|
-
} = options;
|
|
4970
|
-
return new Promise((resolve) => {
|
|
4971
|
-
let timedOut = false;
|
|
4972
|
-
let stdout = "";
|
|
4973
|
-
let stderr = "";
|
|
4974
|
-
const child = spawn(command, {
|
|
4975
|
-
cwd,
|
|
4976
|
-
shell: true,
|
|
4977
|
-
windowsHide: true,
|
|
4978
|
-
detached: platform() !== "win32",
|
|
4979
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
4980
|
-
});
|
|
4981
|
-
const stdoutRef = { value: stdout };
|
|
4982
|
-
const stderrRef = { value: stderr };
|
|
4983
|
-
const decoder = platform() === "win32" ? new TextDecoder("ibm866") : null;
|
|
4984
|
-
const decodeChunk = (chunk) => decoder ? decoder.decode(chunk, { stream: true }) : chunk.toString("utf-8");
|
|
4985
|
-
child.stdout?.on("data", (chunk) => {
|
|
4986
|
-
const text = decodeChunk(chunk);
|
|
4987
|
-
const next = stdoutRef.value.length + text.length;
|
|
4988
|
-
if (next > maxBuffer) {
|
|
4989
|
-
stdoutRef.value = stdoutRef.value.slice(stdoutRef.value.length + text.length - maxBuffer) + text;
|
|
4990
|
-
} else {
|
|
4991
|
-
stdoutRef.value = stdoutRef.value + text;
|
|
4992
|
-
}
|
|
4993
|
-
});
|
|
4994
|
-
child.stderr?.on("data", (chunk) => {
|
|
4995
|
-
const text = decodeChunk(chunk);
|
|
4996
|
-
const next = stderrRef.value.length + text.length;
|
|
4997
|
-
if (next > maxBuffer) {
|
|
4998
|
-
stderrRef.value = stderrRef.value.slice(stderrRef.value.length + text.length - maxBuffer) + text;
|
|
4999
|
-
} else {
|
|
5000
|
-
stderrRef.value = stderrRef.value + text;
|
|
5001
|
-
}
|
|
5002
|
-
});
|
|
5003
|
-
const entry = {
|
|
5004
|
-
kill: () => killTree(child)
|
|
5005
|
-
};
|
|
5006
|
-
if (callId) {
|
|
5007
|
-
activeChildren.set(callId, entry);
|
|
5008
|
-
}
|
|
5009
|
-
const timer = setTimeout(() => {
|
|
5010
|
-
timedOut = true;
|
|
5011
|
-
killTree(child);
|
|
5012
|
-
}, timeoutMs);
|
|
5013
|
-
child.on("error", () => {
|
|
5014
|
-
clearTimeout(timer);
|
|
5015
|
-
if (callId)
|
|
5016
|
-
activeChildren.delete(callId);
|
|
5017
|
-
resolve({
|
|
5018
|
-
stdout: stdoutRef.value,
|
|
5019
|
-
stderr: stderrRef.value,
|
|
5020
|
-
code: null,
|
|
5021
|
-
signal: null,
|
|
5022
|
-
timedOut
|
|
5023
|
-
});
|
|
5024
|
-
});
|
|
5025
|
-
child.on("close", (code, signal) => {
|
|
5026
|
-
clearTimeout(timer);
|
|
5027
|
-
if (callId)
|
|
5028
|
-
activeChildren.delete(callId);
|
|
5029
|
-
resolve({ stdout: stdoutRef.value, stderr: stderrRef.value, code, signal, timedOut });
|
|
5030
|
-
});
|
|
5031
|
-
});
|
|
5032
|
-
}
|
|
5033
4946
|
var activeChildren;
|
|
5034
4947
|
var init_runner = __esm(() => {
|
|
5035
4948
|
activeChildren = new Map;
|
|
@@ -5045,6 +4958,16 @@ class ToolExecutor {
|
|
|
5045
4958
|
this.ctx = ctx;
|
|
5046
4959
|
this.pluginManager = pluginManager;
|
|
5047
4960
|
}
|
|
4961
|
+
async executeByName(name, args, ctx) {
|
|
4962
|
+
const prevCtx = this.ctx;
|
|
4963
|
+
if (ctx)
|
|
4964
|
+
this.ctx = ctx;
|
|
4965
|
+
try {
|
|
4966
|
+
return await this.execute({ id: `redirect_${Date.now()}`, name, arguments: args });
|
|
4967
|
+
} finally {
|
|
4968
|
+
this.ctx = prevCtx;
|
|
4969
|
+
}
|
|
4970
|
+
}
|
|
5048
4971
|
async execute(call, signal) {
|
|
5049
4972
|
const tool = this.registry.get(call.name);
|
|
5050
4973
|
if (!tool) {
|
|
@@ -6810,14 +6733,14 @@ var init_command_validator = __esm(() => {
|
|
|
6810
6733
|
});
|
|
6811
6734
|
|
|
6812
6735
|
// src/modules/processes/registry.ts
|
|
6813
|
-
import { spawn
|
|
6814
|
-
import { platform
|
|
6815
|
-
function
|
|
6736
|
+
import { spawn } from "child_process";
|
|
6737
|
+
import { platform } from "os";
|
|
6738
|
+
function killTree(child) {
|
|
6816
6739
|
const pid = child.pid;
|
|
6817
6740
|
if (!pid)
|
|
6818
6741
|
return;
|
|
6819
|
-
if (
|
|
6820
|
-
|
|
6742
|
+
if (platform() === "win32") {
|
|
6743
|
+
spawn("taskkill", ["/pid", String(pid), "/T", "/F"], {
|
|
6821
6744
|
windowsHide: true,
|
|
6822
6745
|
stdio: "ignore"
|
|
6823
6746
|
});
|
|
@@ -6835,6 +6758,7 @@ function killTree2(child) {
|
|
|
6835
6758
|
class ProcessRegistry {
|
|
6836
6759
|
procs = new Map;
|
|
6837
6760
|
children = new Map;
|
|
6761
|
+
exitWaiters = new Map;
|
|
6838
6762
|
start(command, cwd, sessionId) {
|
|
6839
6763
|
const id = `proc_${Date.now()}_${++seq}`;
|
|
6840
6764
|
const entry = {
|
|
@@ -6850,46 +6774,76 @@ class ProcessRegistry {
|
|
|
6850
6774
|
};
|
|
6851
6775
|
this.trimOldEntries();
|
|
6852
6776
|
this.procs.set(id, entry);
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6777
|
+
let child;
|
|
6778
|
+
try {
|
|
6779
|
+
child = spawn(command, {
|
|
6780
|
+
cwd,
|
|
6781
|
+
shell: true,
|
|
6782
|
+
windowsHide: true,
|
|
6783
|
+
detached: platform() !== "win32",
|
|
6784
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
6785
|
+
});
|
|
6786
|
+
} catch (e) {
|
|
6787
|
+
entry.status = "killed";
|
|
6788
|
+
entry.spawnError = e?.message || String(e);
|
|
6789
|
+
entry.log.push(`[process error] ${entry.spawnError}`);
|
|
6790
|
+
return entry;
|
|
6791
|
+
}
|
|
6860
6792
|
this.children.set(id, child);
|
|
6861
6793
|
entry.pid = child.pid ?? 0;
|
|
6862
|
-
|
|
6794
|
+
const decoder = platform() === "win32" ? new TextDecoder("ibm866") : null;
|
|
6795
|
+
let buf = "";
|
|
6796
|
+
const pushLine = (line) => {
|
|
6797
|
+
if (entry.log.length >= MAX_LOG_LINES) {
|
|
6798
|
+
entry.log.shift();
|
|
6799
|
+
}
|
|
6800
|
+
entry.log.push(line);
|
|
6801
|
+
};
|
|
6863
6802
|
const append = (chunk) => {
|
|
6864
|
-
const
|
|
6865
|
-
|
|
6866
|
-
|
|
6803
|
+
const decoded = decoder ? decoder.decode(chunk, { stream: true }) : chunk.toString();
|
|
6804
|
+
buf += decoded;
|
|
6805
|
+
const lines = buf.split(/\r?\n/);
|
|
6806
|
+
buf = lines.pop() ?? "";
|
|
6867
6807
|
for (const line of lines) {
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6808
|
+
pushLine(line);
|
|
6809
|
+
}
|
|
6810
|
+
};
|
|
6811
|
+
const flush = () => {
|
|
6812
|
+
if (decoder) {
|
|
6813
|
+
buf += decoder.decode();
|
|
6814
|
+
}
|
|
6815
|
+
if (buf) {
|
|
6816
|
+
pushLine(buf);
|
|
6817
|
+
buf = "";
|
|
6872
6818
|
}
|
|
6873
6819
|
};
|
|
6820
|
+
const resolveExit = () => {
|
|
6821
|
+
this.exitWaiters.get(id)?.resolve();
|
|
6822
|
+
};
|
|
6874
6823
|
child.stdout?.on("data", append);
|
|
6875
6824
|
child.stderr?.on("data", append);
|
|
6876
6825
|
child.on("error", (err) => {
|
|
6877
6826
|
if (entry.status === "running") {
|
|
6878
6827
|
entry.status = "killed";
|
|
6879
6828
|
}
|
|
6829
|
+
entry.spawnError = err.message;
|
|
6880
6830
|
append(Buffer.from(`[process error] ${err.message}`));
|
|
6831
|
+
resolveExit();
|
|
6881
6832
|
});
|
|
6882
6833
|
child.on("close", (code) => {
|
|
6834
|
+
flush();
|
|
6883
6835
|
if (entry.status === "running") {
|
|
6884
6836
|
entry.status = "exited";
|
|
6885
6837
|
}
|
|
6886
6838
|
entry.exitCode = code;
|
|
6887
6839
|
this.children.delete(id);
|
|
6888
|
-
|
|
6889
|
-
entry.log.push(partial);
|
|
6890
|
-
partial = "";
|
|
6891
|
-
}
|
|
6840
|
+
resolveExit();
|
|
6892
6841
|
});
|
|
6842
|
+
let resolve10;
|
|
6843
|
+
const promise = new Promise((r) => {
|
|
6844
|
+
resolve10 = r;
|
|
6845
|
+
});
|
|
6846
|
+
this.exitWaiters.set(id, { promise, resolve: resolve10 });
|
|
6893
6847
|
return entry;
|
|
6894
6848
|
}
|
|
6895
6849
|
list(sessionId) {
|
|
@@ -6917,7 +6871,7 @@ class ProcessRegistry {
|
|
|
6917
6871
|
entry.status = "killed";
|
|
6918
6872
|
const child = this.children.get(id);
|
|
6919
6873
|
if (child) {
|
|
6920
|
-
|
|
6874
|
+
killTree(child);
|
|
6921
6875
|
}
|
|
6922
6876
|
}
|
|
6923
6877
|
return true;
|
|
@@ -6929,71 +6883,61 @@ class ProcessRegistry {
|
|
|
6929
6883
|
}
|
|
6930
6884
|
return targets.length;
|
|
6931
6885
|
}
|
|
6886
|
+
waitForExit(id, timeoutMs) {
|
|
6887
|
+
const entry = this.procs.get(id);
|
|
6888
|
+
const waiter = this.exitWaiters.get(id);
|
|
6889
|
+
if (!entry || entry.status !== "running" || !waiter) {
|
|
6890
|
+
return Promise.resolve(true);
|
|
6891
|
+
}
|
|
6892
|
+
return new Promise((resolve10) => {
|
|
6893
|
+
const timer = setTimeout(() => resolve10(false), timeoutMs);
|
|
6894
|
+
waiter.promise.then(() => {
|
|
6895
|
+
clearTimeout(timer);
|
|
6896
|
+
resolve10(true);
|
|
6897
|
+
});
|
|
6898
|
+
});
|
|
6899
|
+
}
|
|
6900
|
+
remove(id) {
|
|
6901
|
+
const entry = this.procs.get(id);
|
|
6902
|
+
if (!entry)
|
|
6903
|
+
return false;
|
|
6904
|
+
if (entry.status === "running") {
|
|
6905
|
+
const child = this.children.get(id);
|
|
6906
|
+
if (child) {
|
|
6907
|
+
killTree(child);
|
|
6908
|
+
}
|
|
6909
|
+
entry.status = "killed";
|
|
6910
|
+
}
|
|
6911
|
+
this.procs.delete(id);
|
|
6912
|
+
this.children.delete(id);
|
|
6913
|
+
this.exitWaiters.delete(id);
|
|
6914
|
+
return true;
|
|
6915
|
+
}
|
|
6932
6916
|
trimOldEntries() {
|
|
6933
6917
|
if (this.procs.size < MAX_KEPT_PROCESSES)
|
|
6934
6918
|
return;
|
|
6935
6919
|
const sorted = Array.from(this.procs.values()).sort((a, b) => a.startedAt.localeCompare(b.startedAt));
|
|
6936
6920
|
const toRemove = sorted.slice(0, sorted.length - MAX_KEPT_PROCESSES + 1);
|
|
6937
6921
|
for (const entry of toRemove) {
|
|
6938
|
-
|
|
6939
|
-
const child = this.children.get(entry.id);
|
|
6940
|
-
if (child) {
|
|
6941
|
-
killTree2(child);
|
|
6942
|
-
}
|
|
6943
|
-
}
|
|
6944
|
-
this.procs.delete(entry.id);
|
|
6945
|
-
this.children.delete(entry.id);
|
|
6922
|
+
this.remove(entry.id);
|
|
6946
6923
|
}
|
|
6947
6924
|
}
|
|
6948
6925
|
}
|
|
6949
|
-
var MAX_LOG_LINES =
|
|
6926
|
+
var MAX_LOG_LINES = 2000, MAX_KEPT_PROCESSES = 20, seq = 0, processRegistry;
|
|
6950
6927
|
var init_registry = __esm(() => {
|
|
6951
6928
|
processRegistry = new ProcessRegistry;
|
|
6952
6929
|
});
|
|
6953
6930
|
|
|
6954
|
-
// src/modules/processes/detect.ts
|
|
6955
|
-
function isLongRunningCommand(command) {
|
|
6956
|
-
return LONG_RUNNING_PATTERNS.some((pattern) => pattern.test(command));
|
|
6957
|
-
}
|
|
6958
|
-
var LONG_RUNNING_PATTERNS;
|
|
6959
|
-
var init_detect2 = __esm(() => {
|
|
6960
|
-
LONG_RUNNING_PATTERNS = [
|
|
6961
|
-
/\b(npm|pnpm|yarn|bun|npx)\s+(run\s+)?(dev|start|serve|preview|watch|server)\b/i,
|
|
6962
|
-
/\b(deno|node)\s+.*\b(run\s+)?(dev|serve|watch|server)\b/i,
|
|
6963
|
-
/(^|\s)--watch\b/i,
|
|
6964
|
-
/(^|\s)-w\b/i,
|
|
6965
|
-
/\bnodemon\b/i,
|
|
6966
|
-
/\btsx watch\b/i,
|
|
6967
|
-
/\b(ts-node|tsc)\s+.*--watch\b/i,
|
|
6968
|
-
/\bvite(?!\s+(build|create))\b/i,
|
|
6969
|
-
/\bwebpack(-dev-server|\s+serve)\b/i,
|
|
6970
|
-
/\bastro dev\b/i,
|
|
6971
|
-
/\bnext (dev|start)\b/i,
|
|
6972
|
-
/\bnuxt (dev|start)\b/i,
|
|
6973
|
-
/\bsvelte-kit (dev|preview)\b/i,
|
|
6974
|
-
/\bgatsby develop\b/i,
|
|
6975
|
-
/\bdocker(-compose)?\s+.*\bup\b/i,
|
|
6976
|
-
/\buvicorn\b|\bgunicorn\b/i,
|
|
6977
|
-
/\bpython\s+-m\s+http\.server\b/i,
|
|
6978
|
-
/\bflask run\b/i,
|
|
6979
|
-
/\bphp artisan serve\b/i,
|
|
6980
|
-
/\brails (server|s)\b/i,
|
|
6981
|
-
/\bvitest watch\b/i,
|
|
6982
|
-
/\bjest --watch\b/i
|
|
6983
|
-
];
|
|
6984
|
-
});
|
|
6985
|
-
|
|
6986
6931
|
// src/modules/processes/index.ts
|
|
6987
6932
|
var init_processes = __esm(() => {
|
|
6988
6933
|
init_runner();
|
|
6989
6934
|
init_registry();
|
|
6990
|
-
init_detect2();
|
|
6991
6935
|
});
|
|
6992
6936
|
|
|
6993
6937
|
// src/tools/bash.ts
|
|
6994
|
-
import { platform as
|
|
6938
|
+
import { platform as platform2 } from "os";
|
|
6995
6939
|
function adaptCommandForWindows(command) {
|
|
6996
|
-
if (
|
|
6940
|
+
if (platform2() !== "win32")
|
|
6997
6941
|
return command;
|
|
6998
6942
|
const trimmed = command.trim();
|
|
6999
6943
|
if (trimmed.startsWith("mkdir -p ")) {
|
|
@@ -7002,9 +6946,45 @@ function adaptCommandForWindows(command) {
|
|
|
7002
6946
|
if (trimmed === "mkdir -p" || trimmed.startsWith("mkdir -p ")) {
|
|
7003
6947
|
return trimmed.replace(/mkdir -p/g, "mkdir");
|
|
7004
6948
|
}
|
|
6949
|
+
const firstWord = trimmed.split(/\s+/)[0]?.split(/[\\/]/).pop();
|
|
6950
|
+
const translated = firstWord ? UNIX_TO_WIN_TRANSLATE[firstWord] : undefined;
|
|
6951
|
+
if (translated && !trimmed.includes("|") && !trimmed.includes(">") && !trimmed.includes("&&") && !trimmed.includes(";")) {
|
|
6952
|
+
return trimmed.replace(firstWord, translated);
|
|
6953
|
+
}
|
|
7005
6954
|
return command;
|
|
7006
6955
|
}
|
|
7007
|
-
|
|
6956
|
+
function detectToolCallInBash(command) {
|
|
6957
|
+
const match = command.trim().match(/^([\w-]+)\s+(.+)$/s);
|
|
6958
|
+
if (!match)
|
|
6959
|
+
return null;
|
|
6960
|
+
const tool = match[1];
|
|
6961
|
+
const rest = match[2].trim();
|
|
6962
|
+
if (!/^[a-z][a-z0-9_]+$/.test(tool))
|
|
6963
|
+
return null;
|
|
6964
|
+
if (!rest.includes("=") && !rest.startsWith("{"))
|
|
6965
|
+
return null;
|
|
6966
|
+
return { tool, args: rest };
|
|
6967
|
+
}
|
|
6968
|
+
function parseToolArgs(raw) {
|
|
6969
|
+
const trimmed = raw.trim();
|
|
6970
|
+
if (trimmed.startsWith("{")) {
|
|
6971
|
+
try {
|
|
6972
|
+
return JSON.parse(trimmed);
|
|
6973
|
+
} catch {}
|
|
6974
|
+
}
|
|
6975
|
+
const args = {};
|
|
6976
|
+
const tokens = trimmed.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) ?? [];
|
|
6977
|
+
for (const token of tokens) {
|
|
6978
|
+
const eq = token.indexOf("=");
|
|
6979
|
+
if (eq > 0) {
|
|
6980
|
+
const key = token.slice(0, eq);
|
|
6981
|
+
const value = token.slice(eq + 1);
|
|
6982
|
+
args[key] = value.replace(/^["']|["']$/g, "");
|
|
6983
|
+
}
|
|
6984
|
+
}
|
|
6985
|
+
return args;
|
|
6986
|
+
}
|
|
6987
|
+
var BASH_GRACE_MS = 5000, SPAWN_SETTLE_MS = 100, bashGraceMs, UNIX_TO_WIN_HINTS, UNIX_TO_WIN_TRANSLATE, bashTool;
|
|
7008
6988
|
var init_bash = __esm(() => {
|
|
7009
6989
|
init_command_validator();
|
|
7010
6990
|
init_audit_log();
|
|
@@ -7012,6 +6992,7 @@ var init_bash = __esm(() => {
|
|
|
7012
6992
|
init_security();
|
|
7013
6993
|
init_processes();
|
|
7014
6994
|
init_i18n();
|
|
6995
|
+
bashGraceMs = BASH_GRACE_MS;
|
|
7015
6996
|
UNIX_TO_WIN_HINTS = {
|
|
7016
6997
|
ls: 'Use "dir" or the list_dir tool instead.',
|
|
7017
6998
|
pwd: 'Use "echo %cd%" or the file_info tool instead.',
|
|
@@ -7028,23 +7009,40 @@ var init_bash = __esm(() => {
|
|
|
7028
7009
|
wc: "Use the read_file tool instead.",
|
|
7029
7010
|
diff: "Use the diff tool instead.",
|
|
7030
7011
|
which: 'Use "where" instead.',
|
|
7031
|
-
echo: "echo works on Windows, but avoid pipes (|)."
|
|
7012
|
+
echo: "echo works on Windows, but avoid pipes (|).",
|
|
7013
|
+
"Get-Content": 'The shell is cmd.exe, not PowerShell. Use "type" or the read_file tool instead.',
|
|
7014
|
+
"Select-Object": "The shell is cmd.exe, not PowerShell. Use the read_file tool with offset/limit instead."
|
|
7015
|
+
};
|
|
7016
|
+
UNIX_TO_WIN_TRANSLATE = {
|
|
7017
|
+
ls: "dir",
|
|
7018
|
+
pwd: "echo %cd%",
|
|
7019
|
+
cat: "type",
|
|
7020
|
+
wc: 'find /c /v ""'
|
|
7032
7021
|
};
|
|
7033
7022
|
bashTool = {
|
|
7034
7023
|
name: "bash",
|
|
7035
|
-
description: "Execute a shell command and return its output. Use for running tests, build, git, and shell operations.
|
|
7024
|
+
description: "Execute a shell command and return its output. Use for running tests, build, git, and shell operations. Commands that are still running after a few seconds are automatically moved to the background and return a process id — manage them with process_list, process_log, process_kill. Set background=true to return a process id immediately for commands you know are long-running (dev servers, watchers).",
|
|
7036
7025
|
tags: ["shell", "code"],
|
|
7037
7026
|
parameters: {
|
|
7038
7027
|
type: "object",
|
|
7039
7028
|
properties: {
|
|
7040
7029
|
command: { type: "string", description: "Shell command to execute" },
|
|
7041
7030
|
workdir: { type: "string", description: "Working directory (default: baseDir)" },
|
|
7042
|
-
background: { type: "boolean", description: "
|
|
7031
|
+
background: { type: "boolean", description: "Return a process id immediately without waiting (default: commands still running after a few seconds are auto-promoted to the background)" }
|
|
7043
7032
|
},
|
|
7044
7033
|
required: ["command"]
|
|
7045
7034
|
},
|
|
7046
7035
|
handler: async (ctx, args) => {
|
|
7047
7036
|
const originalCommand = String(args.command);
|
|
7037
|
+
const toolCall = detectToolCallInBash(originalCommand);
|
|
7038
|
+
if (toolCall && toolCall.tool !== "bash" && ctx.toolExecutor) {
|
|
7039
|
+
const redirected = await ctx.toolExecutor.executeByName(toolCall.tool, parseToolArgs(toolCall.args), ctx);
|
|
7040
|
+
return {
|
|
7041
|
+
success: redirected.success,
|
|
7042
|
+
output: `[redirected to tool "${toolCall.tool}"]
|
|
7043
|
+
${redirected.output}`
|
|
7044
|
+
};
|
|
7045
|
+
}
|
|
7048
7046
|
const command = adaptCommandForWindows(originalCommand);
|
|
7049
7047
|
const workdir = args.workdir ? String(args.workdir) : ctx.baseDir;
|
|
7050
7048
|
const appConfig = ctx.config || {};
|
|
@@ -7062,68 +7060,67 @@ Hint: Use the "workdir" parameter to run commands in a specific directory instea
|
|
|
7062
7060
|
if (securityConfig?.logCommands) {
|
|
7063
7061
|
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), false, `Working directory: ${workdir}`);
|
|
7064
7062
|
}
|
|
7065
|
-
const
|
|
7066
|
-
if (
|
|
7067
|
-
|
|
7068
|
-
if (securityConfig?.logCommands) {
|
|
7069
|
-
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), true, `Started in background: ${entry.id} (PID ${entry.pid})`);
|
|
7070
|
-
}
|
|
7071
|
-
const explicit = args.background === true;
|
|
7072
|
-
return {
|
|
7073
|
-
success: true,
|
|
7074
|
-
output: `${t("proc.started", {
|
|
7075
|
-
id: entry.id,
|
|
7076
|
-
pid: entry.pid,
|
|
7077
|
-
command
|
|
7078
|
-
})}${explicit ? "" : `
|
|
7079
|
-
${t("proc.detected_hint")}`}
|
|
7080
|
-
${t("proc.manage_hint", {
|
|
7081
|
-
id: entry.id
|
|
7082
|
-
})}`
|
|
7083
|
-
};
|
|
7063
|
+
const entry = processRegistry.start(command, workdir, ctx.sessionId);
|
|
7064
|
+
if (ctx.activeCallId) {
|
|
7065
|
+
registerKillable(ctx.activeCallId, () => processRegistry.kill(entry.id));
|
|
7084
7066
|
}
|
|
7085
7067
|
try {
|
|
7086
|
-
const
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7068
|
+
const settleMs = args.background === true ? SPAWN_SETTLE_MS : bashGraceMs;
|
|
7069
|
+
const exited = await processRegistry.waitForExit(entry.id, settleMs);
|
|
7070
|
+
if (exited) {
|
|
7071
|
+
if (entry.spawnError) {
|
|
7072
|
+
return {
|
|
7073
|
+
success: false,
|
|
7074
|
+
output: `[process error] ${entry.spawnError}
|
|
7075
|
+
Hint: check the "workdir" path exists and the command is valid for this OS.`
|
|
7076
|
+
};
|
|
7077
|
+
}
|
|
7078
|
+
const code = entry.exitCode;
|
|
7079
|
+
let output2 = entry.log.join(`
|
|
7093
7080
|
`);
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
`
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
|
|
7100
|
-
|
|
7101
|
-
|
|
7102
|
-
|
|
7103
|
-
const firstWord = command.trim().split(/\s+/)[0]?.split(/[\\/]/).pop();
|
|
7104
|
-
const hint = firstWord ? UNIX_TO_WIN_HINTS[firstWord] : undefined;
|
|
7105
|
-
if (hint) {
|
|
7106
|
-
output = `${output}
|
|
7081
|
+
processRegistry.remove(entry.id);
|
|
7082
|
+
if (!output2 && code !== 0) {
|
|
7083
|
+
output2 = `(exit code ${code})`;
|
|
7084
|
+
}
|
|
7085
|
+
if (platform2() === "win32" && code !== 0) {
|
|
7086
|
+
const firstWord = command.trim().split(/\s+/)[0]?.split(/[\\/]/).pop();
|
|
7087
|
+
const hint = firstWord ? UNIX_TO_WIN_HINTS[firstWord] : undefined;
|
|
7088
|
+
if (hint) {
|
|
7089
|
+
output2 = `${output2}
|
|
7107
7090
|
|
|
7108
7091
|
Hint: "${firstWord}" may not work on Windows. ${hint}`;
|
|
7092
|
+
}
|
|
7109
7093
|
}
|
|
7110
|
-
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
const lines = output.split(`
|
|
7094
|
+
if (securityConfig?.logCommands) {
|
|
7095
|
+
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), code === 0, `Working directory: ${workdir}, Output length: ${output2.length}`);
|
|
7096
|
+
}
|
|
7097
|
+
const lines = output2.split(`
|
|
7115
7098
|
`);
|
|
7116
|
-
|
|
7117
|
-
|
|
7099
|
+
if (lines.length > MAX_PREVIEW_LINES) {
|
|
7100
|
+
output2 = lines.slice(0, MAX_PREVIEW_LINES).join(`
|
|
7118
7101
|
`) + `
|
|
7119
7102
|
... (${lines.length - MAX_PREVIEW_LINES} more lines)`;
|
|
7103
|
+
}
|
|
7104
|
+
return { success: code === 0, output: output2 };
|
|
7120
7105
|
}
|
|
7121
|
-
|
|
7122
|
-
|
|
7106
|
+
const explicit = args.background === true;
|
|
7107
|
+
const output = `${t("proc.started", {
|
|
7108
|
+
id: entry.id,
|
|
7109
|
+
pid: entry.pid,
|
|
7110
|
+
command
|
|
7111
|
+
})}${explicit ? "" : `
|
|
7112
|
+
${t("proc.promoted_hint", { ms: settleMs })}`}
|
|
7113
|
+
${t("proc.manage_hint", {
|
|
7114
|
+
id: entry.id
|
|
7115
|
+
})}`;
|
|
7123
7116
|
if (securityConfig?.logCommands) {
|
|
7124
|
-
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command),
|
|
7117
|
+
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), true, `Started in background: ${entry.id} (PID ${entry.pid})`);
|
|
7118
|
+
}
|
|
7119
|
+
return { success: true, output };
|
|
7120
|
+
} finally {
|
|
7121
|
+
if (ctx.activeCallId) {
|
|
7122
|
+
unregisterKillable(ctx.activeCallId);
|
|
7125
7123
|
}
|
|
7126
|
-
return { success: false, output: e.message || String(e) };
|
|
7127
7124
|
}
|
|
7128
7125
|
}
|
|
7129
7126
|
};
|
|
@@ -7423,6 +7420,15 @@ class SessionLogger {
|
|
|
7423
7420
|
iteration
|
|
7424
7421
|
});
|
|
7425
7422
|
}
|
|
7423
|
+
logPlan(event, detail, iteration) {
|
|
7424
|
+
this.session?.appendLog({
|
|
7425
|
+
ts: new Date().toISOString(),
|
|
7426
|
+
type: "plan",
|
|
7427
|
+
content: detail,
|
|
7428
|
+
tool: event,
|
|
7429
|
+
...iteration !== undefined ? { iteration } : {}
|
|
7430
|
+
});
|
|
7431
|
+
}
|
|
7426
7432
|
}
|
|
7427
7433
|
|
|
7428
7434
|
// node_modules/jsonrepair/lib/esm/utils/JSONRepairError.js
|
|
@@ -8548,76 +8554,394 @@ function filterToolsByTags(tools, toolTags) {
|
|
|
8548
8554
|
});
|
|
8549
8555
|
}
|
|
8550
8556
|
|
|
8551
|
-
// src/modules/execution/
|
|
8552
|
-
|
|
8553
|
-
|
|
8554
|
-
|
|
8555
|
-
|
|
8556
|
-
|
|
8557
|
-
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
8566
|
-
|
|
8567
|
-
|
|
8568
|
-
|
|
8569
|
-
|
|
8570
|
-
|
|
8571
|
-
|
|
8572
|
-
|
|
8573
|
-
|
|
8574
|
-
|
|
8575
|
-
|
|
8576
|
-
|
|
8557
|
+
// src/modules/execution/stuck-detector.ts
|
|
8558
|
+
class StuckDetector {
|
|
8559
|
+
threshold;
|
|
8560
|
+
errorThreshold;
|
|
8561
|
+
iterationsOnCurrentStep = 0;
|
|
8562
|
+
currentStepId = null;
|
|
8563
|
+
toolErrors = new Map;
|
|
8564
|
+
currentStepDescription = "";
|
|
8565
|
+
recentToolCalls = [];
|
|
8566
|
+
maxRecentCalls = 10;
|
|
8567
|
+
repetitionThreshold = 3;
|
|
8568
|
+
consecutiveFailures = 0;
|
|
8569
|
+
lastFailedTool = "";
|
|
8570
|
+
lastErrorOutput = "";
|
|
8571
|
+
escalationCount = 0;
|
|
8572
|
+
escalationThreshold = 3;
|
|
8573
|
+
fileRewriteCount = new Map;
|
|
8574
|
+
fileRewriteThreshold = 3;
|
|
8575
|
+
constructor(threshold = 8, errorThreshold = 3) {
|
|
8576
|
+
this.threshold = threshold;
|
|
8577
|
+
this.errorThreshold = errorThreshold;
|
|
8578
|
+
}
|
|
8579
|
+
recordIteration(stepId) {
|
|
8580
|
+
if (stepId === this.currentStepId) {
|
|
8581
|
+
this.iterationsOnCurrentStep++;
|
|
8582
|
+
} else {
|
|
8583
|
+
this.resetStepState(stepId);
|
|
8577
8584
|
}
|
|
8578
8585
|
}
|
|
8579
|
-
|
|
8580
|
-
|
|
8581
|
-
|
|
8582
|
-
|
|
8583
|
-
|
|
8584
|
-
|
|
8585
|
-
return {
|
|
8586
|
-
subtaskId: subtask.id,
|
|
8587
|
-
success: false,
|
|
8588
|
-
summary: `Unknown expert_tag: ${subtask.expert_tag}`,
|
|
8589
|
-
result: "",
|
|
8590
|
-
error: `No expert config found for tag "${subtask.expert_tag}"`,
|
|
8591
|
-
durationMs: Date.now() - startTime
|
|
8592
|
-
};
|
|
8586
|
+
recordToolCall(name, args) {
|
|
8587
|
+
const argsKey = JSON.stringify(args);
|
|
8588
|
+
this.recentToolCalls.push({ name, argsKey });
|
|
8589
|
+
if (this.recentToolCalls.length > this.maxRecentCalls) {
|
|
8590
|
+
this.recentToolCalls.shift();
|
|
8591
|
+
}
|
|
8593
8592
|
}
|
|
8594
|
-
|
|
8595
|
-
|
|
8596
|
-
|
|
8597
|
-
|
|
8598
|
-
|
|
8599
|
-
|
|
8600
|
-
result: "",
|
|
8601
|
-
error: "Subagent tool not found in registry",
|
|
8602
|
-
durationMs: Date.now() - startTime
|
|
8603
|
-
};
|
|
8593
|
+
recordToolError(toolName, output) {
|
|
8594
|
+
this.toolErrors.set(toolName, (this.toolErrors.get(toolName) || 0) + 1);
|
|
8595
|
+
this.consecutiveFailures++;
|
|
8596
|
+
this.lastFailedTool = toolName;
|
|
8597
|
+
if (output)
|
|
8598
|
+
this.lastErrorOutput = output;
|
|
8604
8599
|
}
|
|
8605
|
-
|
|
8606
|
-
|
|
8607
|
-
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8611
|
-
|
|
8612
|
-
|
|
8613
|
-
|
|
8614
|
-
|
|
8615
|
-
|
|
8600
|
+
getLastErrorOutput() {
|
|
8601
|
+
return this.lastErrorOutput;
|
|
8602
|
+
}
|
|
8603
|
+
getLastFailedTool() {
|
|
8604
|
+
return this.lastFailedTool;
|
|
8605
|
+
}
|
|
8606
|
+
getIterationsOnCurrentStep() {
|
|
8607
|
+
return this.iterationsOnCurrentStep;
|
|
8608
|
+
}
|
|
8609
|
+
recordToolSuccess() {
|
|
8610
|
+
this.consecutiveFailures = 0;
|
|
8611
|
+
this.lastFailedTool = "";
|
|
8612
|
+
this.escalationCount = 0;
|
|
8613
|
+
}
|
|
8614
|
+
setCurrentStep(stepId, description) {
|
|
8615
|
+
if (stepId !== this.currentStepId) {
|
|
8616
|
+
this.resetStepState(stepId);
|
|
8617
|
+
}
|
|
8618
|
+
this.currentStepDescription = description;
|
|
8619
|
+
}
|
|
8620
|
+
isStuck() {
|
|
8621
|
+
return this.iterationsOnCurrentStep > this.threshold;
|
|
8622
|
+
}
|
|
8623
|
+
hasRepetitiveErrors(toolName) {
|
|
8624
|
+
if (toolName) {
|
|
8625
|
+
return (this.toolErrors.get(toolName) || 0) >= this.errorThreshold;
|
|
8626
|
+
}
|
|
8627
|
+
return Array.from(this.toolErrors.values()).some((c) => c >= this.errorThreshold);
|
|
8628
|
+
}
|
|
8629
|
+
hasRepetitiveToolCalls() {
|
|
8630
|
+
if (this.recentToolCalls.length < this.repetitionThreshold)
|
|
8631
|
+
return false;
|
|
8632
|
+
const last = this.recentToolCalls[this.recentToolCalls.length - 1];
|
|
8633
|
+
let count = 1;
|
|
8634
|
+
for (let i = this.recentToolCalls.length - 2;i >= 0; i--) {
|
|
8635
|
+
if (this.recentToolCalls[i].name === last.name && this.recentToolCalls[i].argsKey === last.argsKey) {
|
|
8636
|
+
count++;
|
|
8637
|
+
} else {
|
|
8638
|
+
break;
|
|
8639
|
+
}
|
|
8640
|
+
}
|
|
8641
|
+
return count >= this.repetitionThreshold;
|
|
8642
|
+
}
|
|
8643
|
+
hasConsecutiveFailures() {
|
|
8644
|
+
return this.consecutiveFailures >= this.errorThreshold;
|
|
8645
|
+
}
|
|
8646
|
+
getConsecutiveFailuresCount() {
|
|
8647
|
+
return this.consecutiveFailures;
|
|
8648
|
+
}
|
|
8649
|
+
recordEscalation() {
|
|
8650
|
+
this.escalationCount++;
|
|
8651
|
+
}
|
|
8652
|
+
shouldEscalate() {
|
|
8653
|
+
return this.escalationCount >= this.escalationThreshold;
|
|
8654
|
+
}
|
|
8655
|
+
resetEscalation() {
|
|
8656
|
+
this.escalationCount = 0;
|
|
8657
|
+
}
|
|
8658
|
+
getEscalationCount() {
|
|
8659
|
+
return this.escalationCount;
|
|
8660
|
+
}
|
|
8661
|
+
getHints() {
|
|
8662
|
+
const hints = [];
|
|
8663
|
+
const desc = this.currentStepDescription.toLowerCase();
|
|
8664
|
+
if (desc.includes("install") || desc.includes("npm") || desc.includes("pip")) {
|
|
8665
|
+
hints.push("Check if a lock file exists (package-lock.json, poetry.lock). If missing, run the install command first.");
|
|
8666
|
+
}
|
|
8667
|
+
if (desc.includes("test") || desc.includes("spec")) {
|
|
8668
|
+
hints.push("Make sure the source files exist and have real code before running tests.");
|
|
8669
|
+
}
|
|
8670
|
+
if (desc.includes("build") || desc.includes("compile")) {
|
|
8671
|
+
hints.push("Check that all dependencies are installed and source files are not empty.");
|
|
8672
|
+
}
|
|
8673
|
+
if (desc.includes("deploy") || desc.includes("publish")) {
|
|
8674
|
+
hints.push("Verify credentials and network access before deploying.");
|
|
8675
|
+
}
|
|
8676
|
+
if (this.hasRepetitiveToolCalls()) {
|
|
8677
|
+
hints.push("You are calling the same tool repeatedly with the same arguments. Try a different approach.");
|
|
8678
|
+
}
|
|
8679
|
+
if (this.hasConsecutiveFailures()) {
|
|
8680
|
+
hints.push("Multiple different tools are failing. Check if the environment is set up correctly.");
|
|
8681
|
+
}
|
|
8682
|
+
return hints;
|
|
8683
|
+
}
|
|
8684
|
+
getActionableHints() {
|
|
8685
|
+
const hints = [];
|
|
8686
|
+
const error = this.lastErrorOutput;
|
|
8687
|
+
if (!error)
|
|
8688
|
+
return hints;
|
|
8689
|
+
if (/Cannot read properties of undefined|is not a function|is not a constructor/i.test(error)) {
|
|
8690
|
+
if (/node_modules/.test(error)) {
|
|
8691
|
+
hints.push("A dependency is incompatible with your Node.js version. Check if there is an alternative package or use a different runtime.");
|
|
8692
|
+
}
|
|
8693
|
+
}
|
|
8694
|
+
if (/TS1005|TS1003|TS1109|TS1128|TS1434/i.test(error)) {
|
|
8695
|
+
hints.push("TypeScript syntax error. Read the error message carefully — it tells you the exact line and column. Fix the syntax before retrying.");
|
|
8696
|
+
}
|
|
8697
|
+
if (/TS2322|TS2345|TS2769|TS7006|TS7016/i.test(error)) {
|
|
8698
|
+
hints.push("TypeScript type mismatch. Check the type signature of the function/API you are using. If a package lacks type declarations, install @types/<package> or use skipLibCheck.");
|
|
8699
|
+
}
|
|
8700
|
+
if (/Cannot find module|Module not found|ERR_MODULE_NOT_FOUND/i.test(error)) {
|
|
8701
|
+
hints.push("Module not found. Run npm install or check if the import path is correct.");
|
|
8702
|
+
}
|
|
8703
|
+
if (/EACCES|EPERM|permission denied/i.test(error)) {
|
|
8704
|
+
hints.push("Permission denied. Check file permissions or run with appropriate privileges.");
|
|
8705
|
+
}
|
|
8706
|
+
if (/ECONNREFUSED|ETIMEDOUT|ENOTFOUND|fetch failed/i.test(error)) {
|
|
8707
|
+
hints.push("Network error. Check if the server is running and accessible.");
|
|
8708
|
+
}
|
|
8709
|
+
if (/is not recognized|command not found|not found in path/i.test(error)) {
|
|
8710
|
+
hints.push("Command not found. Check if the tool is installed and in PATH.");
|
|
8711
|
+
}
|
|
8712
|
+
if (this.hasExcessiveRewrites()) {
|
|
8713
|
+
const file = this.getExcessiveRewriteFile();
|
|
8714
|
+
hints.push(`File ${file} has been rewritten ${this.getFileRewriteCount(file)} times without success. Stop rewriting and try a fundamentally different approach.`);
|
|
8715
|
+
}
|
|
8716
|
+
return hints;
|
|
8717
|
+
}
|
|
8718
|
+
getToolAlternative() {
|
|
8719
|
+
const tool = this.lastFailedTool;
|
|
8720
|
+
const error = this.lastErrorOutput;
|
|
8721
|
+
if (!tool)
|
|
8722
|
+
return null;
|
|
8723
|
+
if (/Cannot read properties of undefined|is not a function|TypeError|ReferenceError/i.test(error)) {
|
|
8724
|
+
const alternatives = {
|
|
8725
|
+
"ts-node": "tsx",
|
|
8726
|
+
jest: "vitest",
|
|
8727
|
+
mocha: "vitest",
|
|
8728
|
+
webpack: "vite",
|
|
8729
|
+
rollup: "vite",
|
|
8730
|
+
parcel: "vite",
|
|
8731
|
+
babel: "tsc",
|
|
8732
|
+
eslint: "biome",
|
|
8733
|
+
prettier: "biome"
|
|
8734
|
+
};
|
|
8735
|
+
for (const [failed, alt] of Object.entries(alternatives)) {
|
|
8736
|
+
if (tool.includes(failed) || error.includes(failed)) {
|
|
8737
|
+
return alt;
|
|
8738
|
+
}
|
|
8739
|
+
}
|
|
8740
|
+
}
|
|
8741
|
+
return null;
|
|
8742
|
+
}
|
|
8743
|
+
recordFileRewrite(filePath) {
|
|
8744
|
+
const count = (this.fileRewriteCount.get(filePath) || 0) + 1;
|
|
8745
|
+
this.fileRewriteCount.set(filePath, count);
|
|
8746
|
+
}
|
|
8747
|
+
getFileRewriteCount(filePath) {
|
|
8748
|
+
return this.fileRewriteCount.get(filePath) || 0;
|
|
8749
|
+
}
|
|
8750
|
+
hasExcessiveRewrites() {
|
|
8751
|
+
return Array.from(this.fileRewriteCount.values()).some((c) => c >= this.fileRewriteThreshold);
|
|
8752
|
+
}
|
|
8753
|
+
getExcessiveRewriteFile() {
|
|
8754
|
+
for (const [file, count] of this.fileRewriteCount) {
|
|
8755
|
+
if (count >= this.fileRewriteThreshold)
|
|
8756
|
+
return file;
|
|
8757
|
+
}
|
|
8758
|
+
return null;
|
|
8759
|
+
}
|
|
8760
|
+
getRepetitiveToolMessage() {
|
|
8761
|
+
if (!this.hasRepetitiveToolCalls())
|
|
8762
|
+
return "";
|
|
8763
|
+
const last = this.recentToolCalls[this.recentToolCalls.length - 1];
|
|
8764
|
+
return t("exec.repetitive_tool", {
|
|
8765
|
+
tool: last.name,
|
|
8766
|
+
count: this.repetitionThreshold
|
|
8767
|
+
});
|
|
8768
|
+
}
|
|
8769
|
+
getStuckReason() {
|
|
8770
|
+
if (this.isStuck()) {
|
|
8771
|
+
return t("exec.stuck", {
|
|
8772
|
+
stepId: String(this.currentStepId ?? "?"),
|
|
8773
|
+
description: this.currentStepDescription,
|
|
8774
|
+
iterations: this.iterationsOnCurrentStep
|
|
8775
|
+
});
|
|
8776
|
+
}
|
|
8777
|
+
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
8778
|
+
if (errorTool) {
|
|
8779
|
+
return t("exec.tool_errors", { tool: errorTool[0], count: errorTool[1] });
|
|
8780
|
+
}
|
|
8781
|
+
return "";
|
|
8782
|
+
}
|
|
8783
|
+
getRecoveryMessage() {
|
|
8784
|
+
if (this.isStuck()) {
|
|
8785
|
+
return t("exec.stuck_recovery", {
|
|
8786
|
+
iterations: this.iterationsOnCurrentStep,
|
|
8787
|
+
stepId: String(this.currentStepId ?? "?"),
|
|
8788
|
+
description: this.currentStepDescription
|
|
8789
|
+
});
|
|
8790
|
+
}
|
|
8791
|
+
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
8792
|
+
if (errorTool) {
|
|
8793
|
+
return t("exec.tool_errors_recovery", {
|
|
8794
|
+
tool: errorTool[0],
|
|
8795
|
+
count: errorTool[1]
|
|
8796
|
+
});
|
|
8797
|
+
}
|
|
8798
|
+
if (this.hasConsecutiveFailures()) {
|
|
8799
|
+
return t("exec.consecutive_failures_recovery", {
|
|
8800
|
+
count: this.consecutiveFailures
|
|
8801
|
+
});
|
|
8802
|
+
}
|
|
8803
|
+
if (this.hasRepetitiveToolCalls()) {
|
|
8804
|
+
return this.getRepetitiveToolMessage();
|
|
8805
|
+
}
|
|
8806
|
+
return "";
|
|
8807
|
+
}
|
|
8808
|
+
checkOffTrack(toolName, call) {
|
|
8809
|
+
if (!this.currentStepDescription)
|
|
8810
|
+
return null;
|
|
8811
|
+
const toolArgStr = JSON.stringify(call.arguments).toLowerCase();
|
|
8812
|
+
const pathInStep = (this.currentStepDescription.match(/\b[\w./-]+\.[a-z]+/gi) || []).map((p) => p.toLowerCase());
|
|
8813
|
+
const pathInCall = (toolArgStr.match(/\b[\w./-]+\.[a-z]+/gi) || []).map((p) => p.toLowerCase());
|
|
8814
|
+
const overlaps = pathInCall.some((p) => pathInStep.some((s) => p.includes(s) || s.includes(p)));
|
|
8815
|
+
if (!overlaps && pathInStep.length > 0 && pathInCall.length > 0) {
|
|
8816
|
+
return t("exec.off_track", {
|
|
8817
|
+
stepId: String(this.currentStepId),
|
|
8818
|
+
description: this.currentStepDescription,
|
|
8819
|
+
tool: toolName
|
|
8820
|
+
});
|
|
8821
|
+
}
|
|
8822
|
+
return null;
|
|
8823
|
+
}
|
|
8824
|
+
reset() {
|
|
8825
|
+
this.currentStepId = null;
|
|
8826
|
+
this.iterationsOnCurrentStep = 0;
|
|
8827
|
+
this.toolErrors.clear();
|
|
8828
|
+
this.consecutiveFailures = 0;
|
|
8829
|
+
this.lastFailedTool = "";
|
|
8830
|
+
this.lastErrorOutput = "";
|
|
8831
|
+
this.recentToolCalls = [];
|
|
8832
|
+
this.fileRewriteCount.clear();
|
|
8833
|
+
this.escalationCount = 0;
|
|
8834
|
+
}
|
|
8835
|
+
resetStepState(stepId) {
|
|
8836
|
+
this.currentStepId = stepId;
|
|
8837
|
+
this.iterationsOnCurrentStep = 1;
|
|
8838
|
+
this.toolErrors.clear();
|
|
8839
|
+
this.consecutiveFailures = 0;
|
|
8840
|
+
this.lastFailedTool = "";
|
|
8841
|
+
this.lastErrorOutput = "";
|
|
8842
|
+
this.recentToolCalls = [];
|
|
8843
|
+
this.fileRewriteCount.clear();
|
|
8844
|
+
this.escalationCount = 0;
|
|
8845
|
+
}
|
|
8846
|
+
}
|
|
8847
|
+
var init_stuck_detector = __esm(() => {
|
|
8848
|
+
init_i18n();
|
|
8849
|
+
});
|
|
8850
|
+
|
|
8851
|
+
// src/modules/skills/error-skill-map.ts
|
|
8852
|
+
function suggestSkill(errorOutput) {
|
|
8853
|
+
for (const { pattern, skill } of ERROR_SKILL_MAP) {
|
|
8854
|
+
if (pattern.test(errorOutput))
|
|
8855
|
+
return skill;
|
|
8856
|
+
}
|
|
8857
|
+
return null;
|
|
8858
|
+
}
|
|
8859
|
+
var ERROR_SKILL_MAP;
|
|
8860
|
+
var init_error_skill_map = __esm(() => {
|
|
8861
|
+
ERROR_SKILL_MAP = [
|
|
8862
|
+
{ pattern: /TS\d{4}:/i, skill: "typescript-expert" },
|
|
8863
|
+
{ pattern: /Cannot find name|No overload matches/i, skill: "typescript-type-expert" },
|
|
8864
|
+
{ pattern: /ts-node|tsx.*error/i, skill: "typescript-expert" },
|
|
8865
|
+
{ pattern: /Module not found|Cannot resolve module/i, skill: "typescript-expert" },
|
|
8866
|
+
{ pattern: /ECONNREFUSED|ETIMEDOUT|fetch failed/i, skill: "devops-expert" },
|
|
8867
|
+
{ pattern: /permission denied|EACCES/i, skill: "linux-server-expert" },
|
|
8868
|
+
{ pattern: /docker|container/i, skill: "docker-expert" },
|
|
8869
|
+
{ pattern: /jest|vitest|test.*fail/i, skill: "vitest-testing-expert" }
|
|
8870
|
+
];
|
|
8871
|
+
});
|
|
8872
|
+
|
|
8873
|
+
// src/modules/execution/moe-executor.ts
|
|
8874
|
+
function isTransientError(error) {
|
|
8875
|
+
return TRANSIENT_ERROR_PATTERNS.some((p) => error.includes(p));
|
|
8876
|
+
}
|
|
8877
|
+
function sleep(ms) {
|
|
8878
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
8879
|
+
}
|
|
8880
|
+
function topologicalSort(subtasks) {
|
|
8881
|
+
const sorted = [];
|
|
8882
|
+
const remaining = new Set(subtasks.map((s) => s.id));
|
|
8883
|
+
const subtaskMap = new Map(subtasks.map((s) => [s.id, s]));
|
|
8884
|
+
while (remaining.size > 0) {
|
|
8885
|
+
const ready = [];
|
|
8886
|
+
for (const id of remaining) {
|
|
8887
|
+
const sub = subtaskMap.get(id);
|
|
8888
|
+
const depsSatisfied = (sub.depends_on || []).every((d) => !remaining.has(d));
|
|
8889
|
+
if (depsSatisfied) {
|
|
8890
|
+
ready.push(sub);
|
|
8891
|
+
}
|
|
8892
|
+
}
|
|
8893
|
+
if (ready.length === 0) {
|
|
8894
|
+
break;
|
|
8895
|
+
}
|
|
8896
|
+
sorted.push(ready);
|
|
8897
|
+
for (const r of ready) {
|
|
8898
|
+
remaining.delete(r.id);
|
|
8899
|
+
}
|
|
8900
|
+
}
|
|
8901
|
+
return sorted;
|
|
8902
|
+
}
|
|
8903
|
+
async function executeSubtask(subtask, deps, _sharedContext) {
|
|
8904
|
+
const startTime = Date.now();
|
|
8905
|
+
const expertConfig = getExpertConfig(deps.config, subtask.expert_tag);
|
|
8906
|
+
if (!expertConfig) {
|
|
8907
|
+
return {
|
|
8908
|
+
subtaskId: subtask.id,
|
|
8909
|
+
success: false,
|
|
8910
|
+
summary: `Unknown expert_tag: ${subtask.expert_tag}`,
|
|
8911
|
+
result: "",
|
|
8912
|
+
error: `No expert config found for tag "${subtask.expert_tag}"`,
|
|
8913
|
+
durationMs: Date.now() - startTime
|
|
8914
|
+
};
|
|
8915
|
+
}
|
|
8916
|
+
const subagentTool = deps.toolRegistry.get("subagent");
|
|
8917
|
+
if (!subagentTool) {
|
|
8918
|
+
return {
|
|
8919
|
+
subtaskId: subtask.id,
|
|
8920
|
+
success: false,
|
|
8921
|
+
summary: "Subagent tool not registered",
|
|
8922
|
+
result: "",
|
|
8923
|
+
error: "Subagent tool not found in registry",
|
|
8924
|
+
durationMs: Date.now() - startTime
|
|
8925
|
+
};
|
|
8926
|
+
}
|
|
8927
|
+
const allTools = deps.toolRegistry.getAll();
|
|
8928
|
+
const filteredTools = filterToolsByTags(allTools, expertConfig.tool_tags);
|
|
8929
|
+
if (filteredTools.length === 0) {
|
|
8930
|
+
return {
|
|
8931
|
+
subtaskId: subtask.id,
|
|
8932
|
+
success: false,
|
|
8933
|
+
summary: `No tools available for expert "${subtask.expert_tag}" (tags: ${expertConfig.tool_tags.join(", ")})`,
|
|
8934
|
+
result: "",
|
|
8935
|
+
error: "No matching tools",
|
|
8936
|
+
durationMs: Date.now() - startTime
|
|
8937
|
+
};
|
|
8616
8938
|
}
|
|
8617
8939
|
const maxAttempts = expertConfig.max_attempts || 3;
|
|
8618
8940
|
let lastError = "";
|
|
8619
8941
|
let lastResult = null;
|
|
8942
|
+
const stuckDetector = new StuckDetector(maxAttempts * 2, maxAttempts);
|
|
8620
8943
|
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
8944
|
+
stuckDetector.recordIteration(1);
|
|
8621
8945
|
try {
|
|
8622
8946
|
const allowedFiles = subtask.allowed_files || [];
|
|
8623
8947
|
const readOnlyFiles = subtask.read_only_files || [];
|
|
@@ -8655,44 +8979,90 @@ ${(subtask.success_criteria || []).map((c) => `- ${c}`).join(`
|
|
|
8655
8979
|
}
|
|
8656
8980
|
lastError = result.output;
|
|
8657
8981
|
lastResult = result;
|
|
8982
|
+
stuckDetector.recordToolError("subagent", result.output);
|
|
8983
|
+
if (attempt < maxAttempts) {
|
|
8984
|
+
const hints2 = stuckDetector.getActionableHints();
|
|
8985
|
+
const recovery = stuckDetector.getRecoveryMessage();
|
|
8986
|
+
if (hints2.length > 0) {
|
|
8987
|
+
deps.logger.debug(`Hint for subtask "${subtask.id}" retry: ${hints2.join("; ")}`);
|
|
8988
|
+
}
|
|
8989
|
+
if (recovery) {
|
|
8990
|
+
deps.logger.debug(`Recovery for subtask "${subtask.id}": ${recovery}`);
|
|
8991
|
+
}
|
|
8992
|
+
}
|
|
8658
8993
|
if (isTransientError(lastError) && attempt < maxAttempts) {
|
|
8659
8994
|
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 8000);
|
|
8660
8995
|
deps.logger.debug(`Retrying subtask "${subtask.id}" (attempt ${attempt}/${maxAttempts}) after ${delay}ms: transient error`);
|
|
8661
8996
|
await sleep(delay);
|
|
8662
8997
|
continue;
|
|
8663
8998
|
}
|
|
8999
|
+
const hints = stuckDetector.getActionableHints();
|
|
9000
|
+
const suggestedSkill = suggestSkill(lastError);
|
|
9001
|
+
let errorDetail = result.output;
|
|
9002
|
+
if (hints.length > 0) {
|
|
9003
|
+
errorDetail += `
|
|
9004
|
+
|
|
9005
|
+
${t("exec.hints", { hints: hints.map((h) => `- ${h}`).join(`
|
|
9006
|
+
`) })}`;
|
|
9007
|
+
}
|
|
9008
|
+
if (suggestedSkill) {
|
|
9009
|
+
errorDetail += `
|
|
9010
|
+
|
|
9011
|
+
Consider loading the "${suggestedSkill}" skill for expert guidance.`;
|
|
9012
|
+
}
|
|
8664
9013
|
return {
|
|
8665
9014
|
subtaskId: subtask.id,
|
|
8666
9015
|
success: false,
|
|
8667
9016
|
summary: `Failed: ${subtask.id}`,
|
|
8668
9017
|
result: result.output,
|
|
8669
|
-
error:
|
|
9018
|
+
error: errorDetail,
|
|
8670
9019
|
durationMs: Date.now() - startTime
|
|
8671
9020
|
};
|
|
8672
9021
|
} catch (e) {
|
|
8673
9022
|
lastError = e.message;
|
|
9023
|
+
stuckDetector.recordToolError("subagent", e.message);
|
|
8674
9024
|
if (isTransientError(lastError) && attempt < maxAttempts) {
|
|
8675
9025
|
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 8000);
|
|
8676
9026
|
deps.logger.debug(`Retrying subtask "${subtask.id}" (attempt ${attempt}/${maxAttempts}) after ${delay}ms: ${lastError}`);
|
|
8677
9027
|
await sleep(delay);
|
|
8678
9028
|
continue;
|
|
8679
9029
|
}
|
|
9030
|
+
const hints = stuckDetector.getActionableHints();
|
|
9031
|
+
const suggestedSkill = suggestSkill(lastError);
|
|
9032
|
+
let errorDetail = e.message;
|
|
9033
|
+
if (hints.length > 0) {
|
|
9034
|
+
errorDetail += `
|
|
9035
|
+
|
|
9036
|
+
${t("exec.hints", { hints: hints.map((h) => `- ${h}`).join(`
|
|
9037
|
+
`) })}`;
|
|
9038
|
+
}
|
|
9039
|
+
if (suggestedSkill) {
|
|
9040
|
+
errorDetail += `
|
|
9041
|
+
|
|
9042
|
+
Consider loading the "${suggestedSkill}" skill for expert guidance.`;
|
|
9043
|
+
}
|
|
8680
9044
|
return {
|
|
8681
9045
|
subtaskId: subtask.id,
|
|
8682
9046
|
success: false,
|
|
8683
9047
|
summary: `Error: ${subtask.id}`,
|
|
8684
9048
|
result: "",
|
|
8685
|
-
error:
|
|
9049
|
+
error: errorDetail,
|
|
8686
9050
|
durationMs: Date.now() - startTime
|
|
8687
9051
|
};
|
|
8688
9052
|
}
|
|
8689
9053
|
}
|
|
9054
|
+
const stuckReason = stuckDetector.getStuckReason();
|
|
9055
|
+
let finalError = lastError;
|
|
9056
|
+
if (stuckReason) {
|
|
9057
|
+
finalError += `
|
|
9058
|
+
${stuckReason}`;
|
|
9059
|
+
}
|
|
8690
9060
|
return {
|
|
8691
9061
|
subtaskId: subtask.id,
|
|
8692
9062
|
success: false,
|
|
8693
9063
|
summary: `Failed after ${maxAttempts} attempts: ${subtask.id}`,
|
|
8694
9064
|
result: "",
|
|
8695
|
-
error:
|
|
9065
|
+
error: finalError,
|
|
8696
9066
|
durationMs: Date.now() - startTime
|
|
8697
9067
|
};
|
|
8698
9068
|
}
|
|
@@ -8751,6 +9121,9 @@ class MoEExecutor {
|
|
|
8751
9121
|
}
|
|
8752
9122
|
var TRANSIENT_ERROR_PATTERNS;
|
|
8753
9123
|
var init_moe_executor = __esm(() => {
|
|
9124
|
+
init_stuck_detector();
|
|
9125
|
+
init_error_skill_map();
|
|
9126
|
+
init_i18n();
|
|
8754
9127
|
TRANSIENT_ERROR_PATTERNS = [
|
|
8755
9128
|
"timeout",
|
|
8756
9129
|
"Timeout",
|
|
@@ -8772,7 +9145,7 @@ var init_moe_executor = __esm(() => {
|
|
|
8772
9145
|
// src/modules/execution/verifier.ts
|
|
8773
9146
|
import { existsSync as existsSync17 } from "fs";
|
|
8774
9147
|
import { resolve as resolve10, extname as extname2 } from "path";
|
|
8775
|
-
import {
|
|
9148
|
+
import { spawn as spawn2 } from "child_process";
|
|
8776
9149
|
|
|
8777
9150
|
class StepVerifier {
|
|
8778
9151
|
baseDir;
|
|
@@ -8789,12 +9162,7 @@ class StepVerifier {
|
|
|
8789
9162
|
}
|
|
8790
9163
|
async runScript(scriptName) {
|
|
8791
9164
|
try {
|
|
8792
|
-
|
|
8793
|
-
cwd: this.baseDir,
|
|
8794
|
-
encoding: "utf-8",
|
|
8795
|
-
timeout: 60000,
|
|
8796
|
-
stdio: "pipe"
|
|
8797
|
-
});
|
|
9165
|
+
await this.runAsync(`bun run ${scriptName}`, this.baseDir, 60000);
|
|
8798
9166
|
return { passed: true, message: t("verify.script_passed", { script: scriptName }) };
|
|
8799
9167
|
} catch (e) {
|
|
8800
9168
|
return { passed: false, message: t("verify.script_failed", { script: scriptName, message: e.message }) };
|
|
@@ -8806,12 +9174,7 @@ class StepVerifier {
|
|
|
8806
9174
|
return { passed: true, message: "No tsconfig.json found — skipping type check" };
|
|
8807
9175
|
}
|
|
8808
9176
|
try {
|
|
8809
|
-
|
|
8810
|
-
cwd: this.baseDir,
|
|
8811
|
-
encoding: "utf-8",
|
|
8812
|
-
timeout: 60000,
|
|
8813
|
-
stdio: "pipe"
|
|
8814
|
-
});
|
|
9177
|
+
await this.runAsync("npx tsc --noEmit", this.baseDir, 60000);
|
|
8815
9178
|
return { passed: true, message: "TypeScript type check passed" };
|
|
8816
9179
|
} catch (e) {
|
|
8817
9180
|
const stderr = e.stderr?.toString() || e.stdout?.toString() || e.message;
|
|
@@ -8873,7 +9236,7 @@ class StepVerifier {
|
|
|
8873
9236
|
results.push(result);
|
|
8874
9237
|
if (result.passed) {
|
|
8875
9238
|
const fullPath = resolve10(this.baseDir, filePath);
|
|
8876
|
-
if (!this.validateSyntax(fullPath)) {
|
|
9239
|
+
if (!await this.validateSyntax(fullPath)) {
|
|
8877
9240
|
syntaxValid = false;
|
|
8878
9241
|
results.push({ passed: false, message: t("verify.syntax_error", { path: filePath }) });
|
|
8879
9242
|
}
|
|
@@ -8885,11 +9248,11 @@ class StepVerifier {
|
|
|
8885
9248
|
failed: results.filter((r) => !r.passed)
|
|
8886
9249
|
};
|
|
8887
9250
|
}
|
|
8888
|
-
validateSyntax(filePath) {
|
|
9251
|
+
async validateSyntax(filePath) {
|
|
8889
9252
|
const ext = extname2(filePath);
|
|
8890
9253
|
if (ext === ".ts" || ext === ".tsx") {
|
|
8891
9254
|
try {
|
|
8892
|
-
|
|
9255
|
+
await this.runAsync(`npx tsc --noEmit --skipLibCheck ${filePath}`, this.baseDir, 1e4);
|
|
8893
9256
|
return true;
|
|
8894
9257
|
} catch (err) {
|
|
8895
9258
|
if (err.status === 127 || err.message.includes("not found") || err.message.includes("ENOENT")) {
|
|
@@ -8904,7 +9267,7 @@ class StepVerifier {
|
|
|
8904
9267
|
}
|
|
8905
9268
|
if (ext === ".js" || ext === ".jsx") {
|
|
8906
9269
|
try {
|
|
8907
|
-
|
|
9270
|
+
await this.runAsync(`node --check ${filePath}`, this.baseDir, 5000);
|
|
8908
9271
|
return true;
|
|
8909
9272
|
} catch {
|
|
8910
9273
|
return false;
|
|
@@ -8912,6 +9275,44 @@ class StepVerifier {
|
|
|
8912
9275
|
}
|
|
8913
9276
|
return true;
|
|
8914
9277
|
}
|
|
9278
|
+
runAsync(command, cwd, timeoutMs) {
|
|
9279
|
+
return new Promise((resolve11, reject) => {
|
|
9280
|
+
const child = spawn2(command, {
|
|
9281
|
+
cwd,
|
|
9282
|
+
shell: true,
|
|
9283
|
+
windowsHide: true,
|
|
9284
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
9285
|
+
});
|
|
9286
|
+
let stdout = "";
|
|
9287
|
+
let stderr = "";
|
|
9288
|
+
child.stdout?.on("data", (d) => {
|
|
9289
|
+
stdout += d.toString();
|
|
9290
|
+
});
|
|
9291
|
+
child.stderr?.on("data", (d) => {
|
|
9292
|
+
stderr += d.toString();
|
|
9293
|
+
});
|
|
9294
|
+
const timer = setTimeout(() => {
|
|
9295
|
+
child.kill();
|
|
9296
|
+
reject(new Error(`Command timed out after ${timeoutMs}ms`));
|
|
9297
|
+
}, timeoutMs);
|
|
9298
|
+
child.on("error", (err) => {
|
|
9299
|
+
clearTimeout(timer);
|
|
9300
|
+
reject(err);
|
|
9301
|
+
});
|
|
9302
|
+
child.on("close", (code) => {
|
|
9303
|
+
clearTimeout(timer);
|
|
9304
|
+
if (code === 0) {
|
|
9305
|
+
resolve11({ stdout, stderr });
|
|
9306
|
+
} else {
|
|
9307
|
+
const err = new Error(`Command failed with exit code ${code}`);
|
|
9308
|
+
err.stdout = stdout;
|
|
9309
|
+
err.stderr = stderr;
|
|
9310
|
+
err.status = code;
|
|
9311
|
+
reject(err);
|
|
9312
|
+
}
|
|
9313
|
+
});
|
|
9314
|
+
});
|
|
9315
|
+
}
|
|
8915
9316
|
}
|
|
8916
9317
|
var init_verifier = __esm(() => {
|
|
8917
9318
|
init_i18n();
|
|
@@ -9240,6 +9641,17 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9240
9641
|
logger,
|
|
9241
9642
|
sessionManager: sessionManager?.getActiveMeta()
|
|
9242
9643
|
});
|
|
9644
|
+
if (config.session?.baselineCheck !== false) {
|
|
9645
|
+
const verifier = new StepVerifier(baseDir);
|
|
9646
|
+
verifier.runTypeCheck().then((tc) => {
|
|
9647
|
+
if (!tc.passed) {
|
|
9648
|
+
logger.warn(`Baseline typecheck has issues: ${tc.message?.slice(0, 500)}`);
|
|
9649
|
+
onMeta?.(pc.yellow(`
|
|
9650
|
+
⚠ Baseline typecheck has issues
|
|
9651
|
+
`));
|
|
9652
|
+
}
|
|
9653
|
+
}).catch(() => {});
|
|
9654
|
+
}
|
|
9243
9655
|
}
|
|
9244
9656
|
contextManager.addMessage({ role: "user", content: input });
|
|
9245
9657
|
slog.logUser(input);
|
|
@@ -9287,7 +9699,10 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9287
9699
|
logger,
|
|
9288
9700
|
lastUserMessage: input,
|
|
9289
9701
|
contextManager,
|
|
9290
|
-
onMeta
|
|
9702
|
+
onMeta,
|
|
9703
|
+
sessionLog: {
|
|
9704
|
+
plan: (event, detail, iter) => slog.logPlan(event, detail, iter)
|
|
9705
|
+
}
|
|
9291
9706
|
});
|
|
9292
9707
|
if (contextManager.needsCompaction()) {
|
|
9293
9708
|
contextManager.compact();
|
|
@@ -9296,6 +9711,12 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9296
9711
|
}
|
|
9297
9712
|
const currentTokens = contextManager.getEstimatedTokens();
|
|
9298
9713
|
const budget2 = contextManager.getBudget();
|
|
9714
|
+
const quality = contextManager.getQuality();
|
|
9715
|
+
if (quality < QUALITY_TRIGGER_THRESHOLD && contextManager.getCompactionCount() > 0) {
|
|
9716
|
+
contextManager.compact();
|
|
9717
|
+
logger.warn(`Low context quality (${quality}%) — forced compaction`);
|
|
9718
|
+
slog.logCompaction(`quality-triggered compaction (${quality}% < ${QUALITY_TRIGGER_THRESHOLD}%), iteration ${iteration}`, iteration, currentTokens, budget2.history);
|
|
9719
|
+
}
|
|
9299
9720
|
if (currentTokens > budget2.history) {
|
|
9300
9721
|
contextManager.compact();
|
|
9301
9722
|
logger.warn(`Context overflow (${currentTokens} > ${budget2.history}), forced compaction`);
|
|
@@ -9423,6 +9844,14 @@ ${t("tool.truncated", { tokens: Math.ceil(removedChars / 2) })}`;
|
|
|
9423
9844
|
const duration = Date.now() - startTime;
|
|
9424
9845
|
if (!result.success)
|
|
9425
9846
|
anyToolFailed = true;
|
|
9847
|
+
if (result.success && call.arguments.path) {
|
|
9848
|
+
const filePath = String(call.arguments.path);
|
|
9849
|
+
if (call.name === "write_file" || call.name === "edit_file") {
|
|
9850
|
+
hallucinationDetector.getConsistencyCheck().trackCreatedFile(filePath);
|
|
9851
|
+
} else if (call.name === "delete_file") {
|
|
9852
|
+
hallucinationDetector.getConsistencyCheck().trackDeletedFile(filePath);
|
|
9853
|
+
}
|
|
9854
|
+
}
|
|
9426
9855
|
pluginManager.runOnToolEnd({ iteration, logger }, { id: call.id, name: call.name, arguments: call.arguments }, result, duration);
|
|
9427
9856
|
if (result.display) {
|
|
9428
9857
|
onMeta?.(`
|
|
@@ -9507,14 +9936,15 @@ ${taskReminder}</system-summary>`
|
|
|
9507
9936
|
const ctxBar = pc.green("█".repeat(filled)) + pc.dim("░".repeat(barLen - filled));
|
|
9508
9937
|
const pctColor = ctxPct >= 75 ? pc.yellow : pc.dim;
|
|
9509
9938
|
const compCount = contextManager.getCompactionCount();
|
|
9510
|
-
const
|
|
9511
|
-
const qualityColor =
|
|
9939
|
+
const quality2 = contextManager.getQuality();
|
|
9940
|
+
const qualityColor = quality2 >= 70 ? pc.green : quality2 >= 40 ? pc.yellow : pc.red;
|
|
9512
9941
|
onMeta?.(`
|
|
9513
|
-
${ctxBar} ${pctColor(`${ctxPct}%`)} ${pc.dim(`ctx: ${ctxTokens}/${ctxBudget.history}`)} ${pc.dim(`compactions: ${compCount}`)} ${qualityColor(`quality: ${
|
|
9942
|
+
${ctxBar} ${pctColor(`${ctxPct}%`)} ${pc.dim(`ctx: ${ctxTokens}/${ctxBudget.history}`)} ${pc.dim(`compactions: ${compCount}`)} ${qualityColor(`quality: ${quality2}%`)}
|
|
9514
9943
|
`);
|
|
9515
9944
|
continue;
|
|
9516
9945
|
}
|
|
9517
|
-
|
|
9946
|
+
hallucinationDetector.getConfidenceCheck().setPreviousResponse(lastText);
|
|
9947
|
+
const hallucinationResult = await hallucinationDetector.validate(textContent);
|
|
9518
9948
|
if (hallucinationResult.status === "block") {
|
|
9519
9949
|
logger.warn(`Response blocked: ${hallucinationResult.reason}`);
|
|
9520
9950
|
return {
|
|
@@ -9570,6 +10000,14 @@ ${taskReminder}</system-summary>`
|
|
|
9570
10000
|
slog.logAssistant(textContent, reasoningContent, undefined, iteration);
|
|
9571
10001
|
}
|
|
9572
10002
|
lastText = textContent;
|
|
10003
|
+
{
|
|
10004
|
+
const decisionPatterns = [
|
|
10005
|
+
...textContent.matchAll(/(?:plan|decided|decision|решено|план|решение):\s*(.+?)(?:\n|$)/gi)
|
|
10006
|
+
];
|
|
10007
|
+
for (const match of decisionPatterns) {
|
|
10008
|
+
hallucinationDetector.getConsistencyCheck().trackDecision(match[1].trim(), "agent_response");
|
|
10009
|
+
}
|
|
10010
|
+
}
|
|
9573
10011
|
if (!sawToolCall) {
|
|
9574
10012
|
if (this.deps.finalAudit) {
|
|
9575
10013
|
const audit = await this.deps.finalAudit();
|
|
@@ -9677,7 +10115,7 @@ ${taskReminder}</system-summary>`
|
|
|
9677
10115
|
});
|
|
9678
10116
|
}
|
|
9679
10117
|
}
|
|
9680
|
-
var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000;
|
|
10118
|
+
var TOOL_RESULT_MAX_TOKENS_RATIO = 0.3, TOOL_RESULT_ABSOLUTE_MAX_CHARS = 15000, QUALITY_TRIGGER_THRESHOLD = 40;
|
|
9681
10119
|
var init_agent = __esm(() => {
|
|
9682
10120
|
init_i18n();
|
|
9683
10121
|
init_colors();
|
|
@@ -9685,6 +10123,7 @@ var init_agent = __esm(() => {
|
|
|
9685
10123
|
init_processes();
|
|
9686
10124
|
init_agent_moe();
|
|
9687
10125
|
init_store();
|
|
10126
|
+
init_verifier();
|
|
9688
10127
|
});
|
|
9689
10128
|
|
|
9690
10129
|
// src/modules/context/manager.ts
|
|
@@ -9990,53 +10429,16 @@ class ConsistencyCheck {
|
|
|
9990
10429
|
getCreatedFiles() {
|
|
9991
10430
|
return Array.from(this.createdFiles);
|
|
9992
10431
|
}
|
|
9993
|
-
|
|
9994
|
-
|
|
9995
|
-
|
|
9996
|
-
|
|
9997
|
-
|
|
9998
|
-
|
|
9999
|
-
|
|
10000
|
-
|
|
10001
|
-
return true;
|
|
10002
|
-
if (lower.includes(`replacing ${word} with`))
|
|
10003
|
-
return true;
|
|
10004
|
-
if (lower.includes(`switching to`))
|
|
10005
|
-
return true;
|
|
10006
|
-
if (lower.includes(`changing from ${word}`))
|
|
10007
|
-
return true;
|
|
10008
|
-
if (lower.includes(`abandoning ${word}`))
|
|
10009
|
-
return true;
|
|
10010
|
-
if (lower.includes(`вместо ${word}`))
|
|
10011
|
-
return true;
|
|
10012
|
-
if (lower.includes(`заменяя ${word}`))
|
|
10013
|
-
return true;
|
|
10014
|
-
if (lower.includes(`заменяем ${word}`))
|
|
10015
|
-
return true;
|
|
10016
|
-
if (lower.includes(`переключаемся на`))
|
|
10017
|
-
return true;
|
|
10018
|
-
if (lower.includes(`от ${word} к`))
|
|
10019
|
-
return true;
|
|
10020
|
-
if (lower.includes(`отказываемся от ${word}`))
|
|
10021
|
-
return true;
|
|
10022
|
-
return false;
|
|
10023
|
-
});
|
|
10024
|
-
if (contradicts) {
|
|
10025
|
-
return {
|
|
10026
|
-
status: "warn",
|
|
10027
|
-
reason: t("hall.contradiction", {
|
|
10028
|
-
decision: d.decision,
|
|
10029
|
-
location: d.location
|
|
10030
|
-
})
|
|
10031
|
-
};
|
|
10032
|
-
}
|
|
10033
|
-
}
|
|
10034
|
-
return { status: "pass" };
|
|
10432
|
+
getDeletedFiles() {
|
|
10433
|
+
return Array.from(this.deletedFiles);
|
|
10434
|
+
}
|
|
10435
|
+
getDecisions() {
|
|
10436
|
+
return this.decisions;
|
|
10437
|
+
}
|
|
10438
|
+
hasDecisions() {
|
|
10439
|
+
return this.decisions.length > 0;
|
|
10035
10440
|
}
|
|
10036
10441
|
}
|
|
10037
|
-
var init_consistency = __esm(() => {
|
|
10038
|
-
init_i18n();
|
|
10039
|
-
});
|
|
10040
10442
|
|
|
10041
10443
|
// src/modules/hallucination/confidence.ts
|
|
10042
10444
|
class ConfidenceCheck {
|
|
@@ -10078,19 +10480,193 @@ class ConfidenceCheck {
|
|
|
10078
10480
|
};
|
|
10079
10481
|
}
|
|
10080
10482
|
}
|
|
10081
|
-
return { status: "pass" };
|
|
10082
|
-
}
|
|
10083
|
-
calculateOverlap(a, b) {
|
|
10084
|
-
const wordsA = new Set(a.toLowerCase().split(/\s+/));
|
|
10085
|
-
const wordsB = b.toLowerCase().split(/\s+/);
|
|
10086
|
-
if (wordsB.length === 0)
|
|
10087
|
-
return 0;
|
|
10088
|
-
const matches = wordsB.filter((w) => wordsA.has(w));
|
|
10089
|
-
return matches.length / wordsB.length;
|
|
10483
|
+
return { status: "pass" };
|
|
10484
|
+
}
|
|
10485
|
+
calculateOverlap(a, b) {
|
|
10486
|
+
const wordsA = new Set(a.toLowerCase().split(/\s+/));
|
|
10487
|
+
const wordsB = b.toLowerCase().split(/\s+/);
|
|
10488
|
+
if (wordsB.length === 0)
|
|
10489
|
+
return 0;
|
|
10490
|
+
const matches = wordsB.filter((w) => wordsA.has(w));
|
|
10491
|
+
return matches.length / wordsB.length;
|
|
10492
|
+
}
|
|
10493
|
+
}
|
|
10494
|
+
var MIN_CHARS = 1, MIN_WORDS = 5;
|
|
10495
|
+
var init_confidence = __esm(() => {
|
|
10496
|
+
init_i18n();
|
|
10497
|
+
});
|
|
10498
|
+
|
|
10499
|
+
// src/modules/hallucination/factual.ts
|
|
10500
|
+
import { existsSync as existsSync20 } from "fs";
|
|
10501
|
+
import { resolve as resolve11, isAbsolute } from "path";
|
|
10502
|
+
function looksLikeFilePath(s) {
|
|
10503
|
+
const lower = s.toLowerCase();
|
|
10504
|
+
if (TECH_NAMES.has(lower))
|
|
10505
|
+
return false;
|
|
10506
|
+
if (!s.includes("/") && !s.includes("\\") && !s.match(/^\w+\.[a-z]{2,4}$/i))
|
|
10507
|
+
return false;
|
|
10508
|
+
const ext = s.split(".").pop()?.toLowerCase() || "";
|
|
10509
|
+
return !NON_FILE_EXTENSIONS.has(ext);
|
|
10510
|
+
}
|
|
10511
|
+
|
|
10512
|
+
class FactualCheck {
|
|
10513
|
+
baseDir;
|
|
10514
|
+
constructor(baseDir) {
|
|
10515
|
+
this.baseDir = baseDir;
|
|
10516
|
+
}
|
|
10517
|
+
validate(response) {
|
|
10518
|
+
const filePaths = this.extractFilePaths(response);
|
|
10519
|
+
if (filePaths.length === 0)
|
|
10520
|
+
return { status: "pass" };
|
|
10521
|
+
const nonExistent = [];
|
|
10522
|
+
for (const fp of filePaths) {
|
|
10523
|
+
const abs = isAbsolute(fp) ? fp : resolve11(this.baseDir, fp);
|
|
10524
|
+
if (!existsSync20(abs)) {
|
|
10525
|
+
nonExistent.push(fp);
|
|
10526
|
+
}
|
|
10527
|
+
}
|
|
10528
|
+
if (nonExistent.length > 0) {
|
|
10529
|
+
return {
|
|
10530
|
+
status: "warn",
|
|
10531
|
+
reason: t("hall.unknown_paths", {
|
|
10532
|
+
paths: nonExistent.slice(0, 5).join(", ")
|
|
10533
|
+
})
|
|
10534
|
+
};
|
|
10535
|
+
}
|
|
10536
|
+
return { status: "pass" };
|
|
10537
|
+
}
|
|
10538
|
+
extractFilePaths(text) {
|
|
10539
|
+
const pattern = /\b(?:[a-zA-Z]:[\\/])?[\w./\\-]+\.[a-z]{2,6}\b/gi;
|
|
10540
|
+
const matches = text.match(pattern) || [];
|
|
10541
|
+
return [...new Set(matches)].filter(looksLikeFilePath);
|
|
10542
|
+
}
|
|
10543
|
+
}
|
|
10544
|
+
var NON_FILE_EXTENSIONS, TECH_NAMES;
|
|
10545
|
+
var init_factual = __esm(() => {
|
|
10546
|
+
init_i18n();
|
|
10547
|
+
NON_FILE_EXTENSIONS = new Set([
|
|
10548
|
+
"com",
|
|
10549
|
+
"org",
|
|
10550
|
+
"net",
|
|
10551
|
+
"gov",
|
|
10552
|
+
"edu",
|
|
10553
|
+
"io",
|
|
10554
|
+
"dev",
|
|
10555
|
+
"app",
|
|
10556
|
+
"co",
|
|
10557
|
+
"ai",
|
|
10558
|
+
"ru",
|
|
10559
|
+
"de",
|
|
10560
|
+
"fr",
|
|
10561
|
+
"uk",
|
|
10562
|
+
"us",
|
|
10563
|
+
"es",
|
|
10564
|
+
"it",
|
|
10565
|
+
"jp",
|
|
10566
|
+
"cn",
|
|
10567
|
+
"br"
|
|
10568
|
+
]);
|
|
10569
|
+
TECH_NAMES = new Set([
|
|
10570
|
+
"node.js",
|
|
10571
|
+
"react.js",
|
|
10572
|
+
"vue.js",
|
|
10573
|
+
"next.js",
|
|
10574
|
+
"angular.js",
|
|
10575
|
+
"express.js",
|
|
10576
|
+
"deno.js",
|
|
10577
|
+
"qwik.js",
|
|
10578
|
+
"svelte.js",
|
|
10579
|
+
"jquery.js"
|
|
10580
|
+
]);
|
|
10581
|
+
});
|
|
10582
|
+
|
|
10583
|
+
// src/modules/hallucination/llm-judge.ts
|
|
10584
|
+
class LLMJudge {
|
|
10585
|
+
provider;
|
|
10586
|
+
constructor(provider) {
|
|
10587
|
+
this.provider = provider;
|
|
10588
|
+
}
|
|
10589
|
+
async validate(response, consistency) {
|
|
10590
|
+
const decisions = consistency.getDecisions();
|
|
10591
|
+
if (decisions.length === 0) {
|
|
10592
|
+
return { status: "pass" };
|
|
10593
|
+
}
|
|
10594
|
+
const shown = decisions.slice(-MAX_DECISIONS_SHOWN);
|
|
10595
|
+
const decisionLines = shown.map((d, i) => `${i + 1}. [${d.location || "agent"}] "${d.decision}"`).join(`
|
|
10596
|
+
`);
|
|
10597
|
+
const system = `You are a consistency checker for an AI coding agent. The agent previously made these decisions:
|
|
10598
|
+
|
|
10599
|
+
${decisionLines}
|
|
10600
|
+
|
|
10601
|
+
Decide whether the agent's NEW response contradicts any of these decisions. A contradiction means reversing or abandoning a previously stated approach (e.g. switching to a different framework, changing a chosen strategy, or undoing a decision that was explicitly made).
|
|
10602
|
+
|
|
10603
|
+
Reply with ONLY valid JSON, no other text:
|
|
10604
|
+
- {"contradicts": false}
|
|
10605
|
+
- {"contradicts": true, "reason": "short explanation"}`;
|
|
10606
|
+
const user = `Agent's new response:
|
|
10607
|
+
|
|
10608
|
+
${response.slice(0, MAX_RESPONSE_CHARS)}`;
|
|
10609
|
+
try {
|
|
10610
|
+
const verdictText = await this.askJudge([
|
|
10611
|
+
{ role: "system", content: system },
|
|
10612
|
+
{ role: "user", content: user }
|
|
10613
|
+
]);
|
|
10614
|
+
const verdict = this.parseVerdict(verdictText);
|
|
10615
|
+
if (verdict.contradicts) {
|
|
10616
|
+
const reason = verdict.reason?.trim() ? ` — ${verdict.reason.trim()}` : "";
|
|
10617
|
+
return {
|
|
10618
|
+
status: "warn",
|
|
10619
|
+
reason: t("hall.contradiction_llm", { reason })
|
|
10620
|
+
};
|
|
10621
|
+
}
|
|
10622
|
+
return { status: "pass" };
|
|
10623
|
+
} catch {
|
|
10624
|
+
return { status: "pass" };
|
|
10625
|
+
}
|
|
10626
|
+
}
|
|
10627
|
+
async askJudge(messages) {
|
|
10628
|
+
let text = "";
|
|
10629
|
+
let timer;
|
|
10630
|
+
const consume = (async () => {
|
|
10631
|
+
for await (const chunk of this.provider.chat(messages, [])) {
|
|
10632
|
+
if (chunk.type === "text" && chunk.content) {
|
|
10633
|
+
text += chunk.content;
|
|
10634
|
+
}
|
|
10635
|
+
}
|
|
10636
|
+
return text;
|
|
10637
|
+
})();
|
|
10638
|
+
const timeout = new Promise((_, reject) => {
|
|
10639
|
+
timer = setTimeout(() => reject(new Error("LLM judge timed out")), JUDGE_TIMEOUT_MS);
|
|
10640
|
+
});
|
|
10641
|
+
try {
|
|
10642
|
+
return await Promise.race([consume, timeout]);
|
|
10643
|
+
} finally {
|
|
10644
|
+
if (timer)
|
|
10645
|
+
clearTimeout(timer);
|
|
10646
|
+
}
|
|
10647
|
+
}
|
|
10648
|
+
parseVerdict(text) {
|
|
10649
|
+
const block = text.match(/\{[\s\S]*\}/);
|
|
10650
|
+
if (block) {
|
|
10651
|
+
try {
|
|
10652
|
+
const obj = JSON.parse(block[0]);
|
|
10653
|
+
if (typeof obj.contradicts === "boolean") {
|
|
10654
|
+
return {
|
|
10655
|
+
contradicts: obj.contradicts,
|
|
10656
|
+
reason: typeof obj.reason === "string" ? obj.reason : undefined
|
|
10657
|
+
};
|
|
10658
|
+
}
|
|
10659
|
+
} catch {}
|
|
10660
|
+
}
|
|
10661
|
+
const lower = text.toLowerCase();
|
|
10662
|
+
if (lower.includes('"contradicts": true') || /^yes\b/.test(lower.trim())) {
|
|
10663
|
+
return { contradicts: true, reason: text.slice(0, 200) };
|
|
10664
|
+
}
|
|
10665
|
+
return { contradicts: false };
|
|
10090
10666
|
}
|
|
10091
10667
|
}
|
|
10092
|
-
var
|
|
10093
|
-
var
|
|
10668
|
+
var JUDGE_TIMEOUT_MS = 15000, MAX_DECISIONS_SHOWN = 6, MAX_RESPONSE_CHARS = 2000;
|
|
10669
|
+
var init_llm_judge = __esm(() => {
|
|
10094
10670
|
init_i18n();
|
|
10095
10671
|
});
|
|
10096
10672
|
|
|
@@ -10098,9 +10674,13 @@ var init_confidence = __esm(() => {
|
|
|
10098
10674
|
class HallucinationDetector {
|
|
10099
10675
|
consistency;
|
|
10100
10676
|
confidence;
|
|
10101
|
-
|
|
10677
|
+
factual;
|
|
10678
|
+
judge;
|
|
10679
|
+
constructor(baseDir, llmProvider) {
|
|
10102
10680
|
this.consistency = new ConsistencyCheck;
|
|
10103
10681
|
this.confidence = new ConfidenceCheck;
|
|
10682
|
+
this.factual = baseDir ? new FactualCheck(baseDir) : null;
|
|
10683
|
+
this.judge = llmProvider ? new LLMJudge(llmProvider) : null;
|
|
10104
10684
|
}
|
|
10105
10685
|
getConsistencyCheck() {
|
|
10106
10686
|
return this.consistency;
|
|
@@ -10108,15 +10688,19 @@ class HallucinationDetector {
|
|
|
10108
10688
|
getConfidenceCheck() {
|
|
10109
10689
|
return this.confidence;
|
|
10110
10690
|
}
|
|
10111
|
-
validate(response) {
|
|
10691
|
+
async validate(response) {
|
|
10112
10692
|
const confidenceResult = this.confidence.validate(response);
|
|
10113
10693
|
if (confidenceResult.status === "retry" || confidenceResult.status === "block") {
|
|
10114
10694
|
return confidenceResult;
|
|
10115
10695
|
}
|
|
10116
|
-
const
|
|
10696
|
+
const factualResult = this.factual?.validate(response);
|
|
10697
|
+
if (factualResult && factualResult.status !== "pass") {
|
|
10698
|
+
return factualResult;
|
|
10699
|
+
}
|
|
10700
|
+
const judgeResult = this.judge ? await this.judge.validate(response, this.consistency) : null;
|
|
10117
10701
|
const warnings = [];
|
|
10118
|
-
if (
|
|
10119
|
-
warnings.push(
|
|
10702
|
+
if (judgeResult && judgeResult.status === "warn")
|
|
10703
|
+
warnings.push(judgeResult.reason || "");
|
|
10120
10704
|
if (confidenceResult.status === "warn")
|
|
10121
10705
|
warnings.push(confidenceResult.reason || "");
|
|
10122
10706
|
if (warnings.length > 0) {
|
|
@@ -10126,8 +10710,9 @@ class HallucinationDetector {
|
|
|
10126
10710
|
}
|
|
10127
10711
|
}
|
|
10128
10712
|
var init_detector = __esm(() => {
|
|
10129
|
-
init_consistency();
|
|
10130
10713
|
init_confidence();
|
|
10714
|
+
init_factual();
|
|
10715
|
+
init_llm_judge();
|
|
10131
10716
|
});
|
|
10132
10717
|
|
|
10133
10718
|
// src/modules/plugins/manager.ts
|
|
@@ -10370,7 +10955,7 @@ var init_subagent = __esm(() => {
|
|
|
10370
10955
|
try {
|
|
10371
10956
|
const subContextManager = new ContextManager(ctx.config.contextWindow, ctx.config.contextBudget);
|
|
10372
10957
|
const subPluginManager = new PluginManager;
|
|
10373
|
-
const hallucinationDetector = new HallucinationDetector;
|
|
10958
|
+
const hallucinationDetector = new HallucinationDetector(ctx.baseDir, ctx.llmProvider);
|
|
10374
10959
|
const systemPrompt = {
|
|
10375
10960
|
content: `You are a sub-agent working on a specific task. ${context ? `Context: ${context}` : ""}`,
|
|
10376
10961
|
priority: "critical",
|
|
@@ -10896,7 +11481,7 @@ async function runStep(ctx, step, params, outputs) {
|
|
|
10896
11481
|
toolExecutor: ctx.toolExecutor,
|
|
10897
11482
|
pluginManager: subPluginManager,
|
|
10898
11483
|
contextManager: subContextManager,
|
|
10899
|
-
hallucinationDetector: new HallucinationDetector,
|
|
11484
|
+
hallucinationDetector: new HallucinationDetector(ctx.baseDir, ctx.llmProvider),
|
|
10900
11485
|
logger: ctx.logger,
|
|
10901
11486
|
baseDir: ctx.baseDir,
|
|
10902
11487
|
scope: ctx.scope,
|
|
@@ -11126,7 +11711,7 @@ class MCPClient {
|
|
|
11126
11711
|
}
|
|
11127
11712
|
}
|
|
11128
11713
|
connectStdio() {
|
|
11129
|
-
return new Promise((
|
|
11714
|
+
return new Promise((resolve12, reject) => {
|
|
11130
11715
|
const child = spawn3(this.config.command, this.config.args || [], {
|
|
11131
11716
|
env: { ...process.env, ...this.config.env },
|
|
11132
11717
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -11144,7 +11729,7 @@ class MCPClient {
|
|
|
11144
11729
|
child.on("spawn", () => {
|
|
11145
11730
|
this._connected = true;
|
|
11146
11731
|
this.process = child;
|
|
11147
|
-
|
|
11732
|
+
resolve12();
|
|
11148
11733
|
});
|
|
11149
11734
|
});
|
|
11150
11735
|
}
|
|
@@ -11213,7 +11798,7 @@ class MCPClient {
|
|
|
11213
11798
|
if (!this.process) {
|
|
11214
11799
|
throw new Error("Not connected to MCP server");
|
|
11215
11800
|
}
|
|
11216
|
-
return new Promise((
|
|
11801
|
+
return new Promise((resolve12, reject) => {
|
|
11217
11802
|
const request = {
|
|
11218
11803
|
jsonrpc: "2.0",
|
|
11219
11804
|
id: Date.now(),
|
|
@@ -11237,7 +11822,7 @@ class MCPClient {
|
|
|
11237
11822
|
if (response.error) {
|
|
11238
11823
|
reject(new Error(response.error.message));
|
|
11239
11824
|
} else {
|
|
11240
|
-
|
|
11825
|
+
resolve12(response.result?.tools || []);
|
|
11241
11826
|
}
|
|
11242
11827
|
return;
|
|
11243
11828
|
}
|
|
@@ -11319,8 +11904,8 @@ class MCPClient {
|
|
|
11319
11904
|
arguments: args
|
|
11320
11905
|
}
|
|
11321
11906
|
};
|
|
11322
|
-
return new Promise((
|
|
11323
|
-
this.pendingResolve =
|
|
11907
|
+
return new Promise((resolve12, reject) => {
|
|
11908
|
+
this.pendingResolve = resolve12;
|
|
11324
11909
|
this.pendingReject = reject;
|
|
11325
11910
|
const timeout = this.config.timeout || 30000;
|
|
11326
11911
|
const timer = setTimeout(() => {
|
|
@@ -11347,7 +11932,7 @@ class MCPClient {
|
|
|
11347
11932
|
if (result.error) {
|
|
11348
11933
|
reject(new Error(result.error.message));
|
|
11349
11934
|
} else {
|
|
11350
|
-
|
|
11935
|
+
resolve12(result.result || result);
|
|
11351
11936
|
}
|
|
11352
11937
|
}).catch((err) => {
|
|
11353
11938
|
clearTimeout(timer);
|
|
@@ -11361,7 +11946,7 @@ class MCPClient {
|
|
|
11361
11946
|
if (!this.process) {
|
|
11362
11947
|
throw new Error("Not connected to MCP server");
|
|
11363
11948
|
}
|
|
11364
|
-
return new Promise((
|
|
11949
|
+
return new Promise((resolve12, reject) => {
|
|
11365
11950
|
const request = {
|
|
11366
11951
|
jsonrpc: "2.0",
|
|
11367
11952
|
id: Date.now(),
|
|
@@ -11389,7 +11974,7 @@ class MCPClient {
|
|
|
11389
11974
|
if (response.error) {
|
|
11390
11975
|
reject(new Error(response.error.message));
|
|
11391
11976
|
} else {
|
|
11392
|
-
|
|
11977
|
+
resolve12(response.result);
|
|
11393
11978
|
}
|
|
11394
11979
|
return;
|
|
11395
11980
|
}
|
|
@@ -12377,14 +12962,14 @@ async function readClipboardImage() {
|
|
|
12377
12962
|
return readClipboardFallback();
|
|
12378
12963
|
}
|
|
12379
12964
|
async function readClipboardFallback() {
|
|
12380
|
-
const { platform:
|
|
12381
|
-
const { execSync:
|
|
12965
|
+
const { platform: platform3 } = await import("os");
|
|
12966
|
+
const { execSync: execSync2 } = await import("child_process");
|
|
12382
12967
|
const { readFileSync: readFileSync13, unlinkSync: unlinkSync3 } = await import("fs");
|
|
12383
12968
|
const { join: join16 } = await import("path");
|
|
12384
12969
|
const tmpPath = join16(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
|
|
12385
12970
|
try {
|
|
12386
|
-
if (
|
|
12387
|
-
|
|
12971
|
+
if (platform3() === "linux") {
|
|
12972
|
+
execSync2(`xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`, { timeout: 5000 });
|
|
12388
12973
|
} else {
|
|
12389
12974
|
return null;
|
|
12390
12975
|
}
|
|
@@ -12459,8 +13044,8 @@ var init_image_utils = __esm(() => {
|
|
|
12459
13044
|
});
|
|
12460
13045
|
|
|
12461
13046
|
// src/tools/attach-image.ts
|
|
12462
|
-
import { existsSync as
|
|
12463
|
-
import { resolve as
|
|
13047
|
+
import { existsSync as existsSync22 } from "fs";
|
|
13048
|
+
import { resolve as resolve12 } from "path";
|
|
12464
13049
|
var attachImageTool;
|
|
12465
13050
|
var init_attach_image = __esm(() => {
|
|
12466
13051
|
init_i18n();
|
|
@@ -12501,8 +13086,8 @@ var init_attach_image = __esm(() => {
|
|
|
12501
13086
|
const result = await loadUrlAsDataUrl(source);
|
|
12502
13087
|
dataUrl = result.dataUrl;
|
|
12503
13088
|
} else {
|
|
12504
|
-
const absPath =
|
|
12505
|
-
if (!
|
|
13089
|
+
const absPath = resolve12(ctx.baseDir, source);
|
|
13090
|
+
if (!existsSync22(absPath)) {
|
|
12506
13091
|
return {
|
|
12507
13092
|
success: false,
|
|
12508
13093
|
output: t("image.not_found", { path: source })
|
|
@@ -12658,12 +13243,12 @@ class ModuleRegistry {
|
|
|
12658
13243
|
}
|
|
12659
13244
|
|
|
12660
13245
|
// src/modules/plugins/loader.ts
|
|
12661
|
-
import { readdirSync as readdirSync5, existsSync as
|
|
13246
|
+
import { readdirSync as readdirSync5, existsSync as existsSync23, statSync as statSync4 } from "fs";
|
|
12662
13247
|
import { join as join16 } from "path";
|
|
12663
13248
|
|
|
12664
13249
|
class PluginLoader {
|
|
12665
13250
|
loadFromDir(dirPath, pluginManager, logger) {
|
|
12666
|
-
if (!
|
|
13251
|
+
if (!existsSync23(dirPath))
|
|
12667
13252
|
return;
|
|
12668
13253
|
const entries = readdirSync5(dirPath);
|
|
12669
13254
|
for (const entry of entries) {
|
|
@@ -12690,9 +13275,9 @@ var init_loader = __esm(() => {
|
|
|
12690
13275
|
});
|
|
12691
13276
|
|
|
12692
13277
|
// src/modules/plugins/builtin/lint-on-write.ts
|
|
12693
|
-
import {
|
|
12694
|
-
import { existsSync as
|
|
12695
|
-
import { resolve as
|
|
13278
|
+
import { spawn as spawn4 } from "child_process";
|
|
13279
|
+
import { existsSync as existsSync24, readFileSync as readFileSync13 } from "fs";
|
|
13280
|
+
import { resolve as resolve13, extname as extname4, join as join17 } from "path";
|
|
12696
13281
|
|
|
12697
13282
|
class LintOnWritePlugin {
|
|
12698
13283
|
name = "lint-on-write";
|
|
@@ -12707,11 +13292,11 @@ class LintOnWritePlugin {
|
|
|
12707
13292
|
const path = String(call.arguments.path || "");
|
|
12708
13293
|
if (!path)
|
|
12709
13294
|
return;
|
|
12710
|
-
const fullPath =
|
|
12711
|
-
if (!
|
|
13295
|
+
const fullPath = resolve13(ctx.baseDir, path);
|
|
13296
|
+
if (!existsSync24(fullPath))
|
|
12712
13297
|
return;
|
|
12713
13298
|
const ext = extname4(fullPath);
|
|
12714
|
-
const syntaxError = this.checkSyntax(fullPath, ext, ctx.baseDir);
|
|
13299
|
+
const syntaxError = await this.checkSyntax(fullPath, ext, ctx.baseDir);
|
|
12715
13300
|
if (syntaxError) {
|
|
12716
13301
|
result.output += `
|
|
12717
13302
|
|
|
@@ -12721,10 +13306,10 @@ class LintOnWritePlugin {
|
|
|
12721
13306
|
await this.runProjectLint(ctx, result);
|
|
12722
13307
|
await this.runProjectTypeCheck(ctx, result);
|
|
12723
13308
|
}
|
|
12724
|
-
checkSyntax(filePath, ext, baseDir) {
|
|
13309
|
+
async checkSyntax(filePath, ext, baseDir) {
|
|
12725
13310
|
if (ext === ".ts" || ext === ".tsx") {
|
|
12726
13311
|
try {
|
|
12727
|
-
|
|
13312
|
+
await runAsync(`npx tsc --noEmit --skipLibCheck "${filePath}"`, baseDir, 1e4);
|
|
12728
13313
|
return null;
|
|
12729
13314
|
} catch (err) {
|
|
12730
13315
|
if (err.status === 127 || err.message.includes("not found") || err.message.includes("ENOENT")) {
|
|
@@ -12741,7 +13326,7 @@ class LintOnWritePlugin {
|
|
|
12741
13326
|
}
|
|
12742
13327
|
if (ext === ".js" || ext === ".jsx") {
|
|
12743
13328
|
try {
|
|
12744
|
-
|
|
13329
|
+
await runAsync(`node --check "${filePath}"`, baseDir, 5000);
|
|
12745
13330
|
return null;
|
|
12746
13331
|
} catch (err) {
|
|
12747
13332
|
const stderr = err.stderr?.toString() || "";
|
|
@@ -12755,7 +13340,7 @@ class LintOnWritePlugin {
|
|
|
12755
13340
|
async runProjectLint(ctx, result) {
|
|
12756
13341
|
try {
|
|
12757
13342
|
const packageJsonPath = join17(ctx.baseDir, "package.json");
|
|
12758
|
-
if (!
|
|
13343
|
+
if (!existsSync24(packageJsonPath)) {
|
|
12759
13344
|
return;
|
|
12760
13345
|
}
|
|
12761
13346
|
const packageJson = JSON.parse(readFileSync13(packageJsonPath, "utf-8"));
|
|
@@ -12764,7 +13349,7 @@ class LintOnWritePlugin {
|
|
|
12764
13349
|
return;
|
|
12765
13350
|
}
|
|
12766
13351
|
ctx.logger.debug(`Running lint: ${lintScript}`);
|
|
12767
|
-
|
|
13352
|
+
await runAsync(lintScript, ctx.baseDir, 30000);
|
|
12768
13353
|
ctx.logger.debug("Lint passed");
|
|
12769
13354
|
} catch (err) {
|
|
12770
13355
|
ctx.logger.warn(`Lint failed: ${err.message}`);
|
|
@@ -12775,7 +13360,7 @@ class LintOnWritePlugin {
|
|
|
12775
13360
|
}
|
|
12776
13361
|
async runProjectTypeCheck(ctx, result) {
|
|
12777
13362
|
const tsconfigPath = join17(ctx.baseDir, "tsconfig.json");
|
|
12778
|
-
if (!
|
|
13363
|
+
if (!existsSync24(tsconfigPath)) {
|
|
12779
13364
|
return;
|
|
12780
13365
|
}
|
|
12781
13366
|
const now = Date.now();
|
|
@@ -12799,7 +13384,7 @@ class LintOnWritePlugin {
|
|
|
12799
13384
|
}
|
|
12800
13385
|
async runTscCheck(baseDir) {
|
|
12801
13386
|
try {
|
|
12802
|
-
|
|
13387
|
+
await runAsync(`npx tsc --noEmit --skipLibCheck`, baseDir, 30000);
|
|
12803
13388
|
return null;
|
|
12804
13389
|
} catch (err) {
|
|
12805
13390
|
if (err.status === 127 || err.message.includes("not found") || err.message.includes("ENOENT")) {
|
|
@@ -12815,6 +13400,44 @@ class LintOnWritePlugin {
|
|
|
12815
13400
|
}
|
|
12816
13401
|
}
|
|
12817
13402
|
}
|
|
13403
|
+
function runAsync(command, cwd, timeoutMs) {
|
|
13404
|
+
return new Promise((resolve14, reject) => {
|
|
13405
|
+
const child = spawn4(command, {
|
|
13406
|
+
cwd,
|
|
13407
|
+
shell: true,
|
|
13408
|
+
windowsHide: true,
|
|
13409
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
13410
|
+
});
|
|
13411
|
+
let stdout = "";
|
|
13412
|
+
let stderr = "";
|
|
13413
|
+
child.stdout?.on("data", (d) => {
|
|
13414
|
+
stdout += d.toString();
|
|
13415
|
+
});
|
|
13416
|
+
child.stderr?.on("data", (d) => {
|
|
13417
|
+
stderr += d.toString();
|
|
13418
|
+
});
|
|
13419
|
+
const timer = setTimeout(() => {
|
|
13420
|
+
child.kill();
|
|
13421
|
+
reject(new Error(`Command timed out after ${timeoutMs}ms`));
|
|
13422
|
+
}, timeoutMs);
|
|
13423
|
+
child.on("error", (err) => {
|
|
13424
|
+
clearTimeout(timer);
|
|
13425
|
+
reject(err);
|
|
13426
|
+
});
|
|
13427
|
+
child.on("close", (code) => {
|
|
13428
|
+
clearTimeout(timer);
|
|
13429
|
+
if (code === 0) {
|
|
13430
|
+
resolve14({ stdout, stderr });
|
|
13431
|
+
} else {
|
|
13432
|
+
const err = new Error(`Command failed with exit code ${code}`);
|
|
13433
|
+
err.stdout = stdout;
|
|
13434
|
+
err.stderr = stderr;
|
|
13435
|
+
err.status = code;
|
|
13436
|
+
reject(err);
|
|
13437
|
+
}
|
|
13438
|
+
});
|
|
13439
|
+
});
|
|
13440
|
+
}
|
|
12818
13441
|
var projectTypeCheckPromise = null, projectTypeCheckTimestamp = 0, TYPE_CHECK_DEBOUNCE_MS = 2000, plugin;
|
|
12819
13442
|
var init_lint_on_write = __esm(() => {
|
|
12820
13443
|
plugin = new LintOnWritePlugin;
|
|
@@ -12859,380 +13482,105 @@ class PlanCreator {
|
|
|
12859
13482
|
"make",
|
|
12860
13483
|
"develop"
|
|
12861
13484
|
];
|
|
12862
|
-
const words = task.split(/\s+/);
|
|
12863
|
-
const hasActionWord = actionWords.some((w) => task.toLowerCase().includes(w));
|
|
12864
|
-
return hasActionWord && words.length > 8;
|
|
12865
|
-
}
|
|
12866
|
-
static createPlan(title, stepDescriptions, baseDir) {
|
|
12867
|
-
const stepCount = stepDescriptions.length;
|
|
12868
|
-
return {
|
|
12869
|
-
id: generatePlanId(),
|
|
12870
|
-
title: `[${stepCount} ст.] ${title}`,
|
|
12871
|
-
steps: stepDescriptions.map((desc, i) => ({
|
|
12872
|
-
id: i + 1,
|
|
12873
|
-
description: desc,
|
|
12874
|
-
status: "pending"
|
|
12875
|
-
})),
|
|
12876
|
-
createdAt: new Date().toISOString(),
|
|
12877
|
-
baseDir
|
|
12878
|
-
};
|
|
12879
|
-
}
|
|
12880
|
-
static toPromptBlock(plan, currentStepIndex) {
|
|
12881
|
-
const date = plan.createdAt.slice(0, 10);
|
|
12882
|
-
const lines = [
|
|
12883
|
-
`[${plan.id}] ${plan.title}`,
|
|
12884
|
-
`Dir: ${plan.baseDir}`,
|
|
12885
|
-
`Created: ${date} | Progress: ${plan.steps.filter((s) => s.status === "done").length}/${plan.steps.length} done, current: step ${currentStepIndex + 1}`,
|
|
12886
|
-
``
|
|
12887
|
-
];
|
|
12888
|
-
for (const step of plan.steps) {
|
|
12889
|
-
const icon = step.status === "done" ? "[x]" : step.status === "in_progress" ? "[*]" : step.status === "failed" ? "[!]" : step.status === "skipped" ? "[-]" : "[ ]";
|
|
12890
|
-
const note = step.note ? ` — ${step.note}` : "";
|
|
12891
|
-
lines.push(`${icon} ${step.id}. ${step.description}${note}`);
|
|
12892
|
-
}
|
|
12893
|
-
return lines.join(`
|
|
12894
|
-
`);
|
|
12895
|
-
}
|
|
12896
|
-
}
|
|
12897
|
-
|
|
12898
|
-
// src/modules/execution/tracker.ts
|
|
12899
|
-
class PlanTracker {
|
|
12900
|
-
plan;
|
|
12901
|
-
currentStepIndex = 0;
|
|
12902
|
-
constructor(plan) {
|
|
12903
|
-
this.plan = plan;
|
|
12904
|
-
}
|
|
12905
|
-
getPlan() {
|
|
12906
|
-
return this.plan;
|
|
12907
|
-
}
|
|
12908
|
-
getCurrentStepIndex() {
|
|
12909
|
-
return this.currentStepIndex;
|
|
12910
|
-
}
|
|
12911
|
-
getCurrentStep() {
|
|
12912
|
-
return this.plan.steps[this.currentStepIndex];
|
|
12913
|
-
}
|
|
12914
|
-
getStep(id) {
|
|
12915
|
-
return this.plan.steps.find((s) => s.id === id);
|
|
12916
|
-
}
|
|
12917
|
-
updateStepStatus(id, status) {
|
|
12918
|
-
const step = this.getStep(id);
|
|
12919
|
-
if (step)
|
|
12920
|
-
step.status = status;
|
|
12921
|
-
}
|
|
12922
|
-
addNote(id, note) {
|
|
12923
|
-
const step = this.getStep(id);
|
|
12924
|
-
if (step)
|
|
12925
|
-
step.note = note;
|
|
12926
|
-
}
|
|
12927
|
-
advance() {
|
|
12928
|
-
if (this.currentStepIndex < this.plan.steps.length - 1) {
|
|
12929
|
-
this.currentStepIndex++;
|
|
12930
|
-
this.plan.steps[this.currentStepIndex].status = "in_progress";
|
|
12931
|
-
return true;
|
|
12932
|
-
}
|
|
12933
|
-
return false;
|
|
12934
|
-
}
|
|
12935
|
-
isComplete() {
|
|
12936
|
-
return this.plan.steps.every((s) => s.status === "done" || s.status === "skipped");
|
|
12937
|
-
}
|
|
12938
|
-
getProgressString() {
|
|
12939
|
-
const done = this.plan.steps.filter((s) => s.status === "done").length;
|
|
12940
|
-
const total = this.plan.steps.length;
|
|
12941
|
-
const pct = total > 0 ? Math.round(done / total * 100) : 0;
|
|
12942
|
-
const barWidth = 10;
|
|
12943
|
-
const filled = Math.round(done / total * barWidth);
|
|
12944
|
-
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
12945
|
-
return `[${this.plan.id}] ${this.plan.title} ${done}/${total} ${bar} ${pct}%`;
|
|
12946
|
-
}
|
|
12947
|
-
toPromptBlock() {
|
|
12948
|
-
return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
|
|
12949
|
-
}
|
|
12950
|
-
}
|
|
12951
|
-
var init_tracker = () => {};
|
|
12952
|
-
|
|
12953
|
-
// src/modules/execution/stuck-detector.ts
|
|
12954
|
-
class StuckDetector {
|
|
12955
|
-
threshold;
|
|
12956
|
-
errorThreshold;
|
|
12957
|
-
iterationsOnCurrentStep = 0;
|
|
12958
|
-
currentStepId = null;
|
|
12959
|
-
toolErrors = new Map;
|
|
12960
|
-
currentStepDescription = "";
|
|
12961
|
-
recentToolCalls = [];
|
|
12962
|
-
maxRecentCalls = 10;
|
|
12963
|
-
repetitionThreshold = 3;
|
|
12964
|
-
consecutiveFailures = 0;
|
|
12965
|
-
lastFailedTool = "";
|
|
12966
|
-
lastErrorOutput = "";
|
|
12967
|
-
escalationCount = 0;
|
|
12968
|
-
escalationThreshold = 3;
|
|
12969
|
-
fileRewriteCount = new Map;
|
|
12970
|
-
fileRewriteThreshold = 3;
|
|
12971
|
-
constructor(threshold = 8, errorThreshold = 3) {
|
|
12972
|
-
this.threshold = threshold;
|
|
12973
|
-
this.errorThreshold = errorThreshold;
|
|
12974
|
-
}
|
|
12975
|
-
recordIteration(stepId) {
|
|
12976
|
-
if (stepId === this.currentStepId) {
|
|
12977
|
-
this.iterationsOnCurrentStep++;
|
|
12978
|
-
} else {
|
|
12979
|
-
this.currentStepId = stepId;
|
|
12980
|
-
this.iterationsOnCurrentStep = 1;
|
|
12981
|
-
}
|
|
12982
|
-
}
|
|
12983
|
-
recordToolCall(name, args) {
|
|
12984
|
-
const argsKey = JSON.stringify(args);
|
|
12985
|
-
this.recentToolCalls.push({ name, argsKey });
|
|
12986
|
-
if (this.recentToolCalls.length > this.maxRecentCalls) {
|
|
12987
|
-
this.recentToolCalls.shift();
|
|
12988
|
-
}
|
|
12989
|
-
}
|
|
12990
|
-
recordToolError(toolName, output) {
|
|
12991
|
-
this.toolErrors.set(toolName, (this.toolErrors.get(toolName) || 0) + 1);
|
|
12992
|
-
this.consecutiveFailures++;
|
|
12993
|
-
this.lastFailedTool = toolName;
|
|
12994
|
-
if (output)
|
|
12995
|
-
this.lastErrorOutput = output;
|
|
12996
|
-
}
|
|
12997
|
-
getLastErrorOutput() {
|
|
12998
|
-
return this.lastErrorOutput;
|
|
12999
|
-
}
|
|
13000
|
-
getLastFailedTool() {
|
|
13001
|
-
return this.lastFailedTool;
|
|
13002
|
-
}
|
|
13003
|
-
getIterationsOnCurrentStep() {
|
|
13004
|
-
return this.iterationsOnCurrentStep;
|
|
13005
|
-
}
|
|
13006
|
-
recordToolSuccess() {
|
|
13007
|
-
this.consecutiveFailures = 0;
|
|
13008
|
-
this.lastFailedTool = "";
|
|
13009
|
-
this.escalationCount = 0;
|
|
13010
|
-
}
|
|
13011
|
-
setCurrentStep(stepId, description) {
|
|
13012
|
-
if (stepId !== this.currentStepId) {
|
|
13013
|
-
this.currentStepId = stepId;
|
|
13014
|
-
this.iterationsOnCurrentStep = 1;
|
|
13015
|
-
}
|
|
13016
|
-
this.currentStepDescription = description;
|
|
13017
|
-
}
|
|
13018
|
-
isStuck() {
|
|
13019
|
-
return this.iterationsOnCurrentStep > this.threshold;
|
|
13020
|
-
}
|
|
13021
|
-
hasRepetitiveErrors(toolName) {
|
|
13022
|
-
if (toolName) {
|
|
13023
|
-
return (this.toolErrors.get(toolName) || 0) >= this.errorThreshold;
|
|
13024
|
-
}
|
|
13025
|
-
return Array.from(this.toolErrors.values()).some((c) => c >= this.errorThreshold);
|
|
13026
|
-
}
|
|
13027
|
-
hasRepetitiveToolCalls() {
|
|
13028
|
-
if (this.recentToolCalls.length < this.repetitionThreshold)
|
|
13029
|
-
return false;
|
|
13030
|
-
const last = this.recentToolCalls[this.recentToolCalls.length - 1];
|
|
13031
|
-
let count = 1;
|
|
13032
|
-
for (let i = this.recentToolCalls.length - 2;i >= 0; i--) {
|
|
13033
|
-
if (this.recentToolCalls[i].name === last.name && this.recentToolCalls[i].argsKey === last.argsKey) {
|
|
13034
|
-
count++;
|
|
13035
|
-
} else {
|
|
13036
|
-
break;
|
|
13037
|
-
}
|
|
13038
|
-
}
|
|
13039
|
-
return count >= this.repetitionThreshold;
|
|
13040
|
-
}
|
|
13041
|
-
hasConsecutiveFailures() {
|
|
13042
|
-
return this.consecutiveFailures >= this.errorThreshold;
|
|
13043
|
-
}
|
|
13044
|
-
getConsecutiveFailuresCount() {
|
|
13045
|
-
return this.consecutiveFailures;
|
|
13046
|
-
}
|
|
13047
|
-
recordEscalation() {
|
|
13048
|
-
this.escalationCount++;
|
|
13049
|
-
}
|
|
13050
|
-
shouldEscalate() {
|
|
13051
|
-
return this.escalationCount >= this.escalationThreshold;
|
|
13052
|
-
}
|
|
13053
|
-
resetEscalation() {
|
|
13054
|
-
this.escalationCount = 0;
|
|
13055
|
-
}
|
|
13056
|
-
getEscalationCount() {
|
|
13057
|
-
return this.escalationCount;
|
|
13058
|
-
}
|
|
13059
|
-
getHints() {
|
|
13060
|
-
const hints = [];
|
|
13061
|
-
const desc = this.currentStepDescription.toLowerCase();
|
|
13062
|
-
if (desc.includes("install") || desc.includes("npm") || desc.includes("pip")) {
|
|
13063
|
-
hints.push("Check if a lock file exists (package-lock.json, poetry.lock). If missing, run the install command first.");
|
|
13064
|
-
}
|
|
13065
|
-
if (desc.includes("test") || desc.includes("spec")) {
|
|
13066
|
-
hints.push("Make sure the source files exist and have real code before running tests.");
|
|
13067
|
-
}
|
|
13068
|
-
if (desc.includes("build") || desc.includes("compile")) {
|
|
13069
|
-
hints.push("Check that all dependencies are installed and source files are not empty.");
|
|
13070
|
-
}
|
|
13071
|
-
if (desc.includes("deploy") || desc.includes("publish")) {
|
|
13072
|
-
hints.push("Verify credentials and network access before deploying.");
|
|
13073
|
-
}
|
|
13074
|
-
if (this.hasRepetitiveToolCalls()) {
|
|
13075
|
-
hints.push("You are calling the same tool repeatedly with the same arguments. Try a different approach.");
|
|
13076
|
-
}
|
|
13077
|
-
if (this.hasConsecutiveFailures()) {
|
|
13078
|
-
hints.push("Multiple different tools are failing. Check if the environment is set up correctly.");
|
|
13079
|
-
}
|
|
13080
|
-
return hints;
|
|
13485
|
+
const words = task.split(/\s+/);
|
|
13486
|
+
const hasActionWord = actionWords.some((w) => task.toLowerCase().includes(w));
|
|
13487
|
+
return hasActionWord && words.length > 8;
|
|
13081
13488
|
}
|
|
13082
|
-
|
|
13083
|
-
const
|
|
13084
|
-
|
|
13085
|
-
|
|
13086
|
-
|
|
13087
|
-
|
|
13088
|
-
|
|
13089
|
-
|
|
13090
|
-
|
|
13091
|
-
|
|
13092
|
-
|
|
13093
|
-
|
|
13094
|
-
}
|
|
13095
|
-
if (/TS2322|TS2345|TS2769|TS7006|TS7016/i.test(error)) {
|
|
13096
|
-
hints.push("TypeScript type mismatch. Check the type signature of the function/API you are using. If a package lacks type declarations, install @types/<package> or use skipLibCheck.");
|
|
13097
|
-
}
|
|
13098
|
-
if (/Cannot find module|Module not found|ERR_MODULE_NOT_FOUND/i.test(error)) {
|
|
13099
|
-
hints.push("Module not found. Run npm install or check if the import path is correct.");
|
|
13100
|
-
}
|
|
13101
|
-
if (/EACCES|EPERM|permission denied/i.test(error)) {
|
|
13102
|
-
hints.push("Permission denied. Check file permissions or run with appropriate privileges.");
|
|
13103
|
-
}
|
|
13104
|
-
if (/ECONNREFUSED|ETIMEDOUT|ENOTFOUND|fetch failed/i.test(error)) {
|
|
13105
|
-
hints.push("Network error. Check if the server is running and accessible.");
|
|
13106
|
-
}
|
|
13107
|
-
if (/is not recognized|command not found|not found in path/i.test(error)) {
|
|
13108
|
-
hints.push("Command not found. Check if the tool is installed and in PATH.");
|
|
13109
|
-
}
|
|
13110
|
-
if (this.hasExcessiveRewrites()) {
|
|
13111
|
-
const file = this.getExcessiveRewriteFile();
|
|
13112
|
-
hints.push(`File ${file} has been rewritten ${this.getFileRewriteCount(file)} times without success. Stop rewriting and try a fundamentally different approach.`);
|
|
13113
|
-
}
|
|
13114
|
-
return hints;
|
|
13489
|
+
static createPlan(title, stepDescriptions, baseDir) {
|
|
13490
|
+
const stepCount = stepDescriptions.length;
|
|
13491
|
+
return {
|
|
13492
|
+
id: generatePlanId(),
|
|
13493
|
+
title: `[${stepCount} ст.] ${title}`,
|
|
13494
|
+
steps: stepDescriptions.map((desc, i) => ({
|
|
13495
|
+
id: i + 1,
|
|
13496
|
+
description: desc,
|
|
13497
|
+
status: "pending"
|
|
13498
|
+
})),
|
|
13499
|
+
createdAt: new Date().toISOString(),
|
|
13500
|
+
baseDir
|
|
13501
|
+
};
|
|
13115
13502
|
}
|
|
13116
|
-
|
|
13117
|
-
const
|
|
13118
|
-
const
|
|
13119
|
-
|
|
13120
|
-
|
|
13121
|
-
|
|
13122
|
-
|
|
13123
|
-
|
|
13124
|
-
|
|
13125
|
-
|
|
13126
|
-
|
|
13127
|
-
|
|
13128
|
-
parcel: "vite",
|
|
13129
|
-
babel: "tsc",
|
|
13130
|
-
eslint: "biome",
|
|
13131
|
-
prettier: "biome"
|
|
13132
|
-
};
|
|
13133
|
-
for (const [failed, alt] of Object.entries(alternatives)) {
|
|
13134
|
-
if (tool.includes(failed) || error.includes(failed)) {
|
|
13135
|
-
return alt;
|
|
13136
|
-
}
|
|
13137
|
-
}
|
|
13503
|
+
static toPromptBlock(plan, currentStepIndex) {
|
|
13504
|
+
const date = plan.createdAt.slice(0, 10);
|
|
13505
|
+
const lines = [
|
|
13506
|
+
`[${plan.id}] ${plan.title}`,
|
|
13507
|
+
`Dir: ${plan.baseDir}`,
|
|
13508
|
+
`Created: ${date} | Progress: ${plan.steps.filter((s) => s.status === "done").length}/${plan.steps.length} done, current: step ${currentStepIndex + 1}`,
|
|
13509
|
+
``
|
|
13510
|
+
];
|
|
13511
|
+
for (const step of plan.steps) {
|
|
13512
|
+
const icon = step.status === "done" ? "[x]" : step.status === "in_progress" ? "[*]" : step.status === "failed" ? "[!]" : step.status === "skipped" ? "[-]" : "[ ]";
|
|
13513
|
+
const note = step.note ? ` — ${step.note}` : "";
|
|
13514
|
+
lines.push(`${icon} ${step.id}. ${step.description}${note}`);
|
|
13138
13515
|
}
|
|
13139
|
-
return
|
|
13516
|
+
return lines.join(`
|
|
13517
|
+
`);
|
|
13140
13518
|
}
|
|
13141
|
-
|
|
13142
|
-
|
|
13143
|
-
|
|
13519
|
+
}
|
|
13520
|
+
|
|
13521
|
+
// src/modules/execution/tracker.ts
|
|
13522
|
+
class PlanTracker {
|
|
13523
|
+
plan;
|
|
13524
|
+
currentStepIndex = 0;
|
|
13525
|
+
constructor(plan) {
|
|
13526
|
+
this.plan = plan;
|
|
13144
13527
|
}
|
|
13145
|
-
|
|
13146
|
-
return this.
|
|
13528
|
+
getPlan() {
|
|
13529
|
+
return this.plan;
|
|
13147
13530
|
}
|
|
13148
|
-
|
|
13149
|
-
return
|
|
13531
|
+
getCurrentStepIndex() {
|
|
13532
|
+
return this.currentStepIndex;
|
|
13150
13533
|
}
|
|
13151
|
-
|
|
13152
|
-
|
|
13153
|
-
if (count >= this.fileRewriteThreshold)
|
|
13154
|
-
return file;
|
|
13155
|
-
}
|
|
13156
|
-
return null;
|
|
13534
|
+
getCurrentStep() {
|
|
13535
|
+
return this.plan.steps[this.currentStepIndex];
|
|
13157
13536
|
}
|
|
13158
|
-
|
|
13159
|
-
|
|
13160
|
-
return "";
|
|
13161
|
-
const last = this.recentToolCalls[this.recentToolCalls.length - 1];
|
|
13162
|
-
return t("exec.repetitive_tool", {
|
|
13163
|
-
tool: last.name,
|
|
13164
|
-
count: this.repetitionThreshold
|
|
13165
|
-
});
|
|
13537
|
+
getStep(id) {
|
|
13538
|
+
return this.plan.steps.find((s) => s.id === id);
|
|
13166
13539
|
}
|
|
13167
|
-
|
|
13168
|
-
|
|
13169
|
-
|
|
13170
|
-
|
|
13171
|
-
description: this.currentStepDescription,
|
|
13172
|
-
iterations: this.iterationsOnCurrentStep
|
|
13173
|
-
});
|
|
13174
|
-
}
|
|
13175
|
-
const errorTool = Array.from(this.toolErrors.entries()).find(([_, c]) => c >= this.errorThreshold);
|
|
13176
|
-
if (errorTool) {
|
|
13177
|
-
return t("exec.tool_errors", { tool: errorTool[0], count: errorTool[1] });
|
|
13178
|
-
}
|
|
13179
|
-
return "";
|
|
13540
|
+
updateStepStatus(id, status) {
|
|
13541
|
+
const step = this.getStep(id);
|
|
13542
|
+
if (step)
|
|
13543
|
+
step.status = status;
|
|
13180
13544
|
}
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
return t("exec.tool_errors_recovery", {
|
|
13192
|
-
tool: errorTool[0],
|
|
13193
|
-
count: errorTool[1]
|
|
13194
|
-
});
|
|
13195
|
-
}
|
|
13196
|
-
if (this.hasConsecutiveFailures()) {
|
|
13197
|
-
return t("exec.consecutive_failures_recovery", {
|
|
13198
|
-
count: this.consecutiveFailures
|
|
13199
|
-
});
|
|
13200
|
-
}
|
|
13201
|
-
if (this.hasRepetitiveToolCalls()) {
|
|
13202
|
-
return this.getRepetitiveToolMessage();
|
|
13545
|
+
addNote(id, note) {
|
|
13546
|
+
const step = this.getStep(id);
|
|
13547
|
+
if (step)
|
|
13548
|
+
step.note = note;
|
|
13549
|
+
}
|
|
13550
|
+
advance() {
|
|
13551
|
+
if (this.currentStepIndex < this.plan.steps.length - 1) {
|
|
13552
|
+
this.currentStepIndex++;
|
|
13553
|
+
this.plan.steps[this.currentStepIndex].status = "in_progress";
|
|
13554
|
+
return true;
|
|
13203
13555
|
}
|
|
13204
|
-
return
|
|
13556
|
+
return false;
|
|
13205
13557
|
}
|
|
13206
|
-
|
|
13207
|
-
|
|
13208
|
-
|
|
13209
|
-
const toolArgStr = JSON.stringify(call.arguments).toLowerCase();
|
|
13210
|
-
const pathInStep = (this.currentStepDescription.match(/\b[\w./-]+\.[a-z]+/gi) || []).map((p) => p.toLowerCase());
|
|
13211
|
-
const pathInCall = (toolArgStr.match(/\b[\w./-]+\.[a-z]+/gi) || []).map((p) => p.toLowerCase());
|
|
13212
|
-
const overlaps = pathInCall.some((p) => pathInStep.some((s) => p.includes(s) || s.includes(p)));
|
|
13213
|
-
if (!overlaps && pathInStep.length > 0 && pathInCall.length > 0) {
|
|
13214
|
-
return t("exec.off_track", {
|
|
13215
|
-
stepId: String(this.currentStepId),
|
|
13216
|
-
description: this.currentStepDescription,
|
|
13217
|
-
tool: toolName
|
|
13218
|
-
});
|
|
13558
|
+
syncCurrentStep() {
|
|
13559
|
+
while (this.currentStepIndex < this.plan.steps.length - 1 && (this.plan.steps[this.currentStepIndex].status === "done" || this.plan.steps[this.currentStepIndex].status === "skipped")) {
|
|
13560
|
+
this.currentStepIndex++;
|
|
13219
13561
|
}
|
|
13220
|
-
return null;
|
|
13221
13562
|
}
|
|
13222
|
-
|
|
13223
|
-
this.
|
|
13224
|
-
|
|
13225
|
-
|
|
13226
|
-
this.
|
|
13563
|
+
isComplete() {
|
|
13564
|
+
return this.plan.steps.every((s) => s.status === "done" || s.status === "skipped");
|
|
13565
|
+
}
|
|
13566
|
+
getProgressString() {
|
|
13567
|
+
const done = this.plan.steps.filter((s) => s.status === "done").length;
|
|
13568
|
+
const total = this.plan.steps.length;
|
|
13569
|
+
const pct = total > 0 ? Math.round(done / total * 100) : 0;
|
|
13570
|
+
const barWidth = 10;
|
|
13571
|
+
const filled = Math.round(done / total * barWidth);
|
|
13572
|
+
const bar = "█".repeat(filled) + "░".repeat(barWidth - filled);
|
|
13573
|
+
return `[${this.plan.id}] ${this.plan.title} ${done}/${total} ${bar} ${pct}%`;
|
|
13574
|
+
}
|
|
13575
|
+
toPromptBlock() {
|
|
13576
|
+
return PlanCreator.toPromptBlock(this.plan, this.currentStepIndex);
|
|
13227
13577
|
}
|
|
13228
13578
|
}
|
|
13229
|
-
var
|
|
13230
|
-
init_i18n();
|
|
13231
|
-
});
|
|
13579
|
+
var init_tracker = () => {};
|
|
13232
13580
|
|
|
13233
13581
|
// src/modules/execution/auditor.ts
|
|
13234
|
-
import { existsSync as
|
|
13235
|
-
import { resolve as
|
|
13582
|
+
import { existsSync as existsSync25 } from "fs";
|
|
13583
|
+
import { resolve as resolve14 } from "path";
|
|
13236
13584
|
|
|
13237
13585
|
class Auditor {
|
|
13238
13586
|
baseDir;
|
|
@@ -13246,8 +13594,8 @@ class Auditor {
|
|
|
13246
13594
|
const missingFiles = [];
|
|
13247
13595
|
const existingFiles = [];
|
|
13248
13596
|
for (const filePath of uniqueFiles) {
|
|
13249
|
-
const resolved =
|
|
13250
|
-
if (
|
|
13597
|
+
const resolved = resolve14(this.baseDir, filePath);
|
|
13598
|
+
if (existsSync25(resolved)) {
|
|
13251
13599
|
existingFiles.push(filePath);
|
|
13252
13600
|
} else {
|
|
13253
13601
|
missingFiles.push(filePath);
|
|
@@ -13282,14 +13630,14 @@ var init_auditor = __esm(() => {
|
|
|
13282
13630
|
});
|
|
13283
13631
|
|
|
13284
13632
|
// src/modules/execution/plan-persister.ts
|
|
13285
|
-
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11, existsSync as
|
|
13633
|
+
import { readFileSync as readFileSync14, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11, existsSync as existsSync26 } from "fs";
|
|
13286
13634
|
import { join as join18 } from "path";
|
|
13287
13635
|
|
|
13288
13636
|
class PlanPersister {
|
|
13289
13637
|
filePath;
|
|
13290
13638
|
constructor(baseDir) {
|
|
13291
13639
|
const mmaDir = join18(baseDir, ".mma");
|
|
13292
|
-
if (!
|
|
13640
|
+
if (!existsSync26(mmaDir)) {
|
|
13293
13641
|
mkdirSync11(mmaDir, { recursive: true });
|
|
13294
13642
|
}
|
|
13295
13643
|
this.filePath = join18(mmaDir, "plan.json");
|
|
@@ -13306,7 +13654,7 @@ class PlanPersister {
|
|
|
13306
13654
|
writeFileSync9(this.filePath, JSON.stringify(file, null, 2), "utf-8");
|
|
13307
13655
|
}
|
|
13308
13656
|
load() {
|
|
13309
|
-
if (!
|
|
13657
|
+
if (!existsSync26(this.filePath))
|
|
13310
13658
|
return null;
|
|
13311
13659
|
try {
|
|
13312
13660
|
const raw = readFileSync14(this.filePath, "utf-8");
|
|
@@ -13323,7 +13671,7 @@ class PlanPersister {
|
|
|
13323
13671
|
}
|
|
13324
13672
|
}
|
|
13325
13673
|
clear() {
|
|
13326
|
-
if (
|
|
13674
|
+
if (existsSync26(this.filePath)) {
|
|
13327
13675
|
writeFileSync9(this.filePath, "", "utf-8");
|
|
13328
13676
|
}
|
|
13329
13677
|
}
|
|
@@ -13394,31 +13742,9 @@ var init_plan_coverage = __esm(() => {
|
|
|
13394
13742
|
]);
|
|
13395
13743
|
});
|
|
13396
13744
|
|
|
13397
|
-
// src/modules/skills/error-skill-map.ts
|
|
13398
|
-
function suggestSkill(errorOutput) {
|
|
13399
|
-
for (const { pattern, skill } of ERROR_SKILL_MAP) {
|
|
13400
|
-
if (pattern.test(errorOutput))
|
|
13401
|
-
return skill;
|
|
13402
|
-
}
|
|
13403
|
-
return null;
|
|
13404
|
-
}
|
|
13405
|
-
var ERROR_SKILL_MAP;
|
|
13406
|
-
var init_error_skill_map = __esm(() => {
|
|
13407
|
-
ERROR_SKILL_MAP = [
|
|
13408
|
-
{ pattern: /TS\d{4}:/i, skill: "typescript-expert" },
|
|
13409
|
-
{ pattern: /Cannot find name|No overload matches/i, skill: "typescript-type-expert" },
|
|
13410
|
-
{ pattern: /ts-node|tsx.*error/i, skill: "typescript-expert" },
|
|
13411
|
-
{ pattern: /Module not found|Cannot resolve module/i, skill: "typescript-expert" },
|
|
13412
|
-
{ pattern: /ECONNREFUSED|ETIMEDOUT|fetch failed/i, skill: "devops-expert" },
|
|
13413
|
-
{ pattern: /permission denied|EACCES/i, skill: "linux-server-expert" },
|
|
13414
|
-
{ pattern: /docker|container/i, skill: "docker-expert" },
|
|
13415
|
-
{ pattern: /jest|vitest|test.*fail/i, skill: "vitest-testing-expert" }
|
|
13416
|
-
];
|
|
13417
|
-
});
|
|
13418
|
-
|
|
13419
13745
|
// src/modules/execution/module.ts
|
|
13420
|
-
import { existsSync as
|
|
13421
|
-
import { resolve as
|
|
13746
|
+
import { existsSync as existsSync27, readFileSync as readFileSync15 } from "fs";
|
|
13747
|
+
import { resolve as resolve15 } from "path";
|
|
13422
13748
|
|
|
13423
13749
|
class ExecutionModule {
|
|
13424
13750
|
name = "execution";
|
|
@@ -13447,10 +13773,7 @@ class ExecutionModule {
|
|
|
13447
13773
|
if (!plan)
|
|
13448
13774
|
return false;
|
|
13449
13775
|
this.tracker = new PlanTracker(plan);
|
|
13450
|
-
|
|
13451
|
-
if (!this.tracker.advance())
|
|
13452
|
-
break;
|
|
13453
|
-
}
|
|
13776
|
+
this.tracker.syncCurrentStep();
|
|
13454
13777
|
return true;
|
|
13455
13778
|
}
|
|
13456
13779
|
getTracker() {
|
|
@@ -13562,9 +13885,11 @@ ${display}`,
|
|
|
13562
13885
|
this.tracker.updateStepStatus(Number(args.step), args.status || "done");
|
|
13563
13886
|
if (args.note)
|
|
13564
13887
|
this.tracker.addNote(Number(args.step), String(args.note));
|
|
13888
|
+
this.tracker.syncCurrentStep();
|
|
13565
13889
|
this.persister.save(this.tracker.getPlan());
|
|
13566
13890
|
const progress = this.tracker.getProgressString();
|
|
13567
13891
|
const display = this.tracker.toPromptBlock();
|
|
13892
|
+
_ctx.sessionLog?.plan("step-update", `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })} | ${progress} | current: step ${this.tracker.getCurrentStepIndex() + 1}`);
|
|
13568
13893
|
return {
|
|
13569
13894
|
success: true,
|
|
13570
13895
|
output: `${t("plan.step_status", { step: String(args.step), status: String(args.status || "done") })}
|
|
@@ -13683,6 +14008,7 @@ Sub-tasks: ${note}`
|
|
|
13683
14008
|
const stuckReason = this.stuckDetector.getStuckReason();
|
|
13684
14009
|
if (stuckReason) {
|
|
13685
14010
|
ctx.logger?.warn(stuckReason);
|
|
14011
|
+
ctx.sessionLog?.plan("stuck-warning", stuckReason, typeof ctx.iteration === "number" ? ctx.iteration : undefined);
|
|
13686
14012
|
}
|
|
13687
14013
|
if (this.stuckDetector.isStuck() || this.stuckDetector.hasRepetitiveToolCalls()) {
|
|
13688
14014
|
const currentIter = typeof ctx.iteration === "number" ? ctx.iteration : 0;
|
|
@@ -13800,7 +14126,7 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
13800
14126
|
}
|
|
13801
14127
|
}
|
|
13802
14128
|
}
|
|
13803
|
-
this.advancePlanIfStepComplete(ctx.contextManager);
|
|
14129
|
+
this.advancePlanIfStepComplete(ctx.contextManager, ctx.sessionLog);
|
|
13804
14130
|
}
|
|
13805
14131
|
}
|
|
13806
14132
|
};
|
|
@@ -13840,7 +14166,7 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
13840
14166
|
tool: call.name
|
|
13841
14167
|
});
|
|
13842
14168
|
}
|
|
13843
|
-
advancePlanIfStepComplete(contextManager) {
|
|
14169
|
+
advancePlanIfStepComplete(contextManager, sessionLog) {
|
|
13844
14170
|
const step = this.tracker?.getCurrentStep();
|
|
13845
14171
|
if (!step)
|
|
13846
14172
|
return;
|
|
@@ -13860,7 +14186,7 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
13860
14186
|
"poetry.lock",
|
|
13861
14187
|
"requirements.txt"
|
|
13862
14188
|
];
|
|
13863
|
-
const hasLockFile = lockFiles.some((f) =>
|
|
14189
|
+
const hasLockFile = lockFiles.some((f) => existsSync27(resolve15(this.baseDir, f)));
|
|
13864
14190
|
if (!hasLockFile) {
|
|
13865
14191
|
if (contextManager) {
|
|
13866
14192
|
contextManager.addMessage({
|
|
@@ -13873,13 +14199,13 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
13873
14199
|
}
|
|
13874
14200
|
if (stepPaths.length === 0)
|
|
13875
14201
|
return;
|
|
13876
|
-
const allExist = stepPaths.every((p) =>
|
|
14202
|
+
const allExist = stepPaths.every((p) => existsSync27(resolve15(this.baseDir, p)));
|
|
13877
14203
|
if (!allExist)
|
|
13878
14204
|
return;
|
|
13879
14205
|
const emptyFiles = [];
|
|
13880
14206
|
for (const p of stepPaths) {
|
|
13881
14207
|
try {
|
|
13882
|
-
const content = readFileSync15(
|
|
14208
|
+
const content = readFileSync15(resolve15(this.baseDir, p), "utf-8");
|
|
13883
14209
|
if (content.trim().length < 10) {
|
|
13884
14210
|
emptyFiles.push(p);
|
|
13885
14211
|
}
|
|
@@ -13896,6 +14222,7 @@ Tool "${this.stuckDetector.getLastFailedTool()}" is failing. Try "${alternative}
|
|
|
13896
14222
|
const hadNext = this.tracker?.advance() ?? false;
|
|
13897
14223
|
if (this.tracker) {
|
|
13898
14224
|
this.persister.save(this.tracker.getPlan());
|
|
14225
|
+
sessionLog?.plan("auto-advance", `Step ${step.id} auto-completed | ${this.tracker.getProgressString()} | current: step ${this.tracker.getCurrentStepIndex() + 1}`);
|
|
13899
14226
|
}
|
|
13900
14227
|
if (contextManager) {
|
|
13901
14228
|
const nextStep = hadNext ? this.tracker?.getCurrentStep() : null;
|
|
@@ -13934,7 +14261,7 @@ var init_module = __esm(() => {
|
|
|
13934
14261
|
});
|
|
13935
14262
|
|
|
13936
14263
|
// src/modules/security/session-encryption.ts
|
|
13937
|
-
import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, existsSync as
|
|
14264
|
+
import { readFileSync as readFileSync16, writeFileSync as writeFileSync10, existsSync as existsSync28, readdirSync as readdirSync6, unlinkSync as unlinkSync3 } from "fs";
|
|
13938
14265
|
import { join as join19 } from "path";
|
|
13939
14266
|
import { homedir as homedir8 } from "os";
|
|
13940
14267
|
|
|
@@ -14017,7 +14344,7 @@ class SessionFileEncryptor {
|
|
|
14017
14344
|
const files = readdirSync6(sessionDir);
|
|
14018
14345
|
for (const file of files) {
|
|
14019
14346
|
const filePath = join19(sessionDir, file);
|
|
14020
|
-
if (
|
|
14347
|
+
if (existsSync28(filePath) && !file.endsWith(".enc")) {
|
|
14021
14348
|
try {
|
|
14022
14349
|
const content = readFileSync16(filePath, "utf8");
|
|
14023
14350
|
const encrypted = this.encryptFileContent(content);
|
|
@@ -14058,7 +14385,7 @@ var init_session_encryption = __esm(() => {
|
|
|
14058
14385
|
|
|
14059
14386
|
// src/modules/session/store.ts
|
|
14060
14387
|
import {
|
|
14061
|
-
existsSync as
|
|
14388
|
+
existsSync as existsSync29,
|
|
14062
14389
|
mkdirSync as mkdirSync12,
|
|
14063
14390
|
readdirSync as readdirSync7,
|
|
14064
14391
|
readFileSync as readFileSync17,
|
|
@@ -14104,7 +14431,7 @@ class SessionStore {
|
|
|
14104
14431
|
return join20(this.sessionDir(id), "session.jsonl");
|
|
14105
14432
|
}
|
|
14106
14433
|
sessionExists(id) {
|
|
14107
|
-
return
|
|
14434
|
+
return existsSync29(this.metaPath(id));
|
|
14108
14435
|
}
|
|
14109
14436
|
saveMeta(id, meta) {
|
|
14110
14437
|
const dir = this.sessionDir(id);
|
|
@@ -14118,7 +14445,7 @@ class SessionStore {
|
|
|
14118
14445
|
}
|
|
14119
14446
|
loadMeta(id) {
|
|
14120
14447
|
const path = this.metaPath(id);
|
|
14121
|
-
if (!
|
|
14448
|
+
if (!existsSync29(path))
|
|
14122
14449
|
return null;
|
|
14123
14450
|
try {
|
|
14124
14451
|
const raw = readFileSync17(path, "utf-8");
|
|
@@ -14148,7 +14475,7 @@ class SessionStore {
|
|
|
14148
14475
|
}
|
|
14149
14476
|
loadHistory(id) {
|
|
14150
14477
|
const path = this.historyPath(id);
|
|
14151
|
-
if (!
|
|
14478
|
+
if (!existsSync29(path))
|
|
14152
14479
|
return [];
|
|
14153
14480
|
try {
|
|
14154
14481
|
const raw = readFileSync17(path, "utf-8");
|
|
@@ -14177,7 +14504,7 @@ class SessionStore {
|
|
|
14177
14504
|
}
|
|
14178
14505
|
loadSessionLog(id) {
|
|
14179
14506
|
const path = this.sessionLogPath(id);
|
|
14180
|
-
if (!
|
|
14507
|
+
if (!existsSync29(path))
|
|
14181
14508
|
return [];
|
|
14182
14509
|
try {
|
|
14183
14510
|
const raw = readFileSync17(path, "utf-8");
|
|
@@ -14193,7 +14520,7 @@ class SessionStore {
|
|
|
14193
14520
|
}
|
|
14194
14521
|
}
|
|
14195
14522
|
listSessions() {
|
|
14196
|
-
if (!
|
|
14523
|
+
if (!existsSync29(this.baseDir))
|
|
14197
14524
|
return [];
|
|
14198
14525
|
const entries = readdirSync7(this.baseDir, { withFileTypes: true });
|
|
14199
14526
|
const sessions = [];
|
|
@@ -14209,7 +14536,7 @@ class SessionStore {
|
|
|
14209
14536
|
}
|
|
14210
14537
|
deleteSession(id) {
|
|
14211
14538
|
const dir = this.sessionDir(id);
|
|
14212
|
-
if (
|
|
14539
|
+
if (existsSync29(dir)) {
|
|
14213
14540
|
rmSync(dir, { recursive: true, force: true });
|
|
14214
14541
|
}
|
|
14215
14542
|
}
|
|
@@ -14221,7 +14548,7 @@ class SessionStore {
|
|
|
14221
14548
|
const updatedAt = new Date(session2.updatedAt);
|
|
14222
14549
|
if (updatedAt < thirtyDaysAgo) {
|
|
14223
14550
|
const historyPath = this.historyPath(session2.id);
|
|
14224
|
-
if (
|
|
14551
|
+
if (existsSync29(historyPath)) {
|
|
14225
14552
|
const content = readFileSync17(historyPath, "utf-8");
|
|
14226
14553
|
const compressed = gzipSync(content);
|
|
14227
14554
|
const gzPath = join20(this.baseDir, `${session2.id}.jsonl.gz`);
|
|
@@ -14431,9 +14758,9 @@ class ProfileCompressor {
|
|
|
14431
14758
|
}
|
|
14432
14759
|
|
|
14433
14760
|
// src/modules/user-profile/profile.ts
|
|
14434
|
-
import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, existsSync as
|
|
14761
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, existsSync as existsSync30, mkdirSync as mkdirSync13 } from "fs";
|
|
14435
14762
|
import { join as join21 } from "path";
|
|
14436
|
-
import { homedir as homedir9, hostname, platform as
|
|
14763
|
+
import { homedir as homedir9, hostname, platform as platform3, type } from "os";
|
|
14437
14764
|
import { env } from "process";
|
|
14438
14765
|
|
|
14439
14766
|
class UserProfile {
|
|
@@ -14445,7 +14772,7 @@ class UserProfile {
|
|
|
14445
14772
|
}
|
|
14446
14773
|
collect() {
|
|
14447
14774
|
this.info = {
|
|
14448
|
-
platform:
|
|
14775
|
+
platform: platform3(),
|
|
14449
14776
|
os: `${type()} ${hostname()}`,
|
|
14450
14777
|
hostname: hostname(),
|
|
14451
14778
|
shell: env.SHELL || env.ComSpec || "unknown",
|
|
@@ -14456,14 +14783,14 @@ class UserProfile {
|
|
|
14456
14783
|
return this.info;
|
|
14457
14784
|
}
|
|
14458
14785
|
save() {
|
|
14459
|
-
if (!
|
|
14786
|
+
if (!existsSync30(this.profileDir)) {
|
|
14460
14787
|
mkdirSync13(this.profileDir, { recursive: true });
|
|
14461
14788
|
}
|
|
14462
14789
|
writeFileSync12(join21(this.profileDir, "profile.json"), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), "utf-8");
|
|
14463
14790
|
}
|
|
14464
14791
|
load() {
|
|
14465
14792
|
const path = join21(this.profileDir, "profile.json");
|
|
14466
|
-
if (!
|
|
14793
|
+
if (!existsSync30(path))
|
|
14467
14794
|
return null;
|
|
14468
14795
|
try {
|
|
14469
14796
|
const data = JSON.parse(readFileSync18(path, "utf-8"));
|
|
@@ -14501,12 +14828,12 @@ class UserProfile {
|
|
|
14501
14828
|
var init_profile = () => {};
|
|
14502
14829
|
|
|
14503
14830
|
// src/modules/skills/loader.ts
|
|
14504
|
-
import { readdirSync as readdirSync8, readFileSync as readFileSync19, existsSync as
|
|
14831
|
+
import { readdirSync as readdirSync8, readFileSync as readFileSync19, existsSync as existsSync31, statSync as statSync5 } from "fs";
|
|
14505
14832
|
import { join as join22 } from "path";
|
|
14506
14833
|
|
|
14507
14834
|
class SkillsLoader {
|
|
14508
14835
|
loadFromDir(dirPath) {
|
|
14509
|
-
if (!
|
|
14836
|
+
if (!existsSync31(dirPath))
|
|
14510
14837
|
return [];
|
|
14511
14838
|
const skills = [];
|
|
14512
14839
|
this.scanDir(dirPath, skills);
|
|
@@ -14795,8 +15122,8 @@ var init_browser2 = __esm(() => {
|
|
|
14795
15122
|
});
|
|
14796
15123
|
|
|
14797
15124
|
// src/modules/lsp/client.ts
|
|
14798
|
-
import { spawn as
|
|
14799
|
-
import { resolve as
|
|
15125
|
+
import { spawn as spawn5, execSync as execSync2 } from "child_process";
|
|
15126
|
+
import { resolve as resolve16 } from "path";
|
|
14800
15127
|
|
|
14801
15128
|
class LspClient {
|
|
14802
15129
|
process = null;
|
|
@@ -14812,7 +15139,7 @@ class LspClient {
|
|
|
14812
15139
|
const timeout = config.timeout ?? DEFAULT_TIMEOUT;
|
|
14813
15140
|
try {
|
|
14814
15141
|
await this.startServer(config, baseDir);
|
|
14815
|
-
const rootUri = this.pathToUri(
|
|
15142
|
+
const rootUri = this.pathToUri(resolve16(baseDir));
|
|
14816
15143
|
const initResult = await this.sendRequest("initialize", {
|
|
14817
15144
|
processId: process.pid,
|
|
14818
15145
|
rootUri,
|
|
@@ -14821,11 +15148,11 @@ class LspClient {
|
|
|
14821
15148
|
}, timeout);
|
|
14822
15149
|
this.initialized = true;
|
|
14823
15150
|
this.sendNotification("initialized", {});
|
|
14824
|
-
const uri = this.pathToUri(
|
|
15151
|
+
const uri = this.pathToUri(resolve16(filePath));
|
|
14825
15152
|
const fs2 = await import("fs");
|
|
14826
15153
|
const content = fs2.readFileSync(filePath, "utf-8");
|
|
14827
|
-
const diagPromise = new Promise((
|
|
14828
|
-
this.diagnosticsResolve =
|
|
15154
|
+
const diagPromise = new Promise((resolve17) => {
|
|
15155
|
+
this.diagnosticsResolve = resolve17;
|
|
14829
15156
|
this.diagnostics = [];
|
|
14830
15157
|
this.diagnosticsTimer = setTimeout(() => {
|
|
14831
15158
|
if (this.diagnosticsResolve) {
|
|
@@ -14852,14 +15179,14 @@ class LspClient {
|
|
|
14852
15179
|
async startServer(config, baseDir) {
|
|
14853
15180
|
if (config.autoInstall === false) {
|
|
14854
15181
|
try {
|
|
14855
|
-
|
|
15182
|
+
execSync2(`where ${config.command}`, { stdio: "pipe", timeout: 3000 });
|
|
14856
15183
|
} catch {
|
|
14857
15184
|
throw new Error(`${config.command} not found in PATH`);
|
|
14858
15185
|
}
|
|
14859
15186
|
}
|
|
14860
|
-
return new Promise((
|
|
15187
|
+
return new Promise((resolve17, reject) => {
|
|
14861
15188
|
const args = config.args ?? [];
|
|
14862
|
-
const proc =
|
|
15189
|
+
const proc = spawn5(config.command, args, {
|
|
14863
15190
|
stdio: ["pipe", "pipe", "pipe"],
|
|
14864
15191
|
env: { ...process.env, ...config.env },
|
|
14865
15192
|
cwd: baseDir
|
|
@@ -14870,7 +15197,7 @@ class LspClient {
|
|
|
14870
15197
|
});
|
|
14871
15198
|
proc.stderr.on("data", () => {});
|
|
14872
15199
|
proc.once("spawn", () => {
|
|
14873
|
-
|
|
15200
|
+
resolve17();
|
|
14874
15201
|
});
|
|
14875
15202
|
this.process = proc;
|
|
14876
15203
|
setTimeout(() => {
|
|
@@ -14942,9 +15269,9 @@ class LspClient {
|
|
|
14942
15269
|
}
|
|
14943
15270
|
}
|
|
14944
15271
|
sendRequest(method, params, timeout) {
|
|
14945
|
-
return new Promise((
|
|
15272
|
+
return new Promise((resolve17, reject) => {
|
|
14946
15273
|
const id = ++this.requestId;
|
|
14947
|
-
this.pending.set(id, { resolve:
|
|
15274
|
+
this.pending.set(id, { resolve: resolve17, reject });
|
|
14948
15275
|
const message = JSON.stringify({ jsonrpc: "2.0", id, method, params });
|
|
14949
15276
|
this.write(message);
|
|
14950
15277
|
setTimeout(() => {
|
|
@@ -15022,8 +15349,8 @@ var DEFAULT_TIMEOUT = 15000;
|
|
|
15022
15349
|
var init_client2 = () => {};
|
|
15023
15350
|
|
|
15024
15351
|
// src/modules/lsp/module.ts
|
|
15025
|
-
import { existsSync as
|
|
15026
|
-
import { resolve as
|
|
15352
|
+
import { existsSync as existsSync32 } from "fs";
|
|
15353
|
+
import { resolve as resolve17 } from "path";
|
|
15027
15354
|
|
|
15028
15355
|
class LspModule {
|
|
15029
15356
|
name = "lsp";
|
|
@@ -15047,8 +15374,8 @@ class LspModule {
|
|
|
15047
15374
|
const filePath = String(call.arguments.path ?? "");
|
|
15048
15375
|
if (!filePath)
|
|
15049
15376
|
return;
|
|
15050
|
-
const fullPath =
|
|
15051
|
-
if (!
|
|
15377
|
+
const fullPath = resolve17(_ctx.baseDir, filePath);
|
|
15378
|
+
if (!existsSync32(fullPath))
|
|
15052
15379
|
return;
|
|
15053
15380
|
const serverConfig = getServerForFile(fullPath, self.config);
|
|
15054
15381
|
if (!serverConfig)
|
|
@@ -15106,7 +15433,7 @@ var init_lsp = __esm(() => {
|
|
|
15106
15433
|
});
|
|
15107
15434
|
|
|
15108
15435
|
// src/modules/indexer/walker.ts
|
|
15109
|
-
import { readdirSync as readdirSync9, readFileSync as readFileSync20, statSync as statSync6, existsSync as
|
|
15436
|
+
import { readdirSync as readdirSync9, readFileSync as readFileSync20, statSync as statSync6, existsSync as existsSync33, watch } from "fs";
|
|
15110
15437
|
import { join as join23, relative, extname as extname5 } from "path";
|
|
15111
15438
|
|
|
15112
15439
|
class Indexer {
|
|
@@ -15134,7 +15461,7 @@ class Indexer {
|
|
|
15134
15461
|
let totalSize = 0;
|
|
15135
15462
|
let count = 0;
|
|
15136
15463
|
const walkDir = (dir) => {
|
|
15137
|
-
if (!
|
|
15464
|
+
if (!existsSync33(dir))
|
|
15138
15465
|
return;
|
|
15139
15466
|
let entries;
|
|
15140
15467
|
try {
|
|
@@ -15208,7 +15535,7 @@ var init_walker = __esm(() => {
|
|
|
15208
15535
|
});
|
|
15209
15536
|
|
|
15210
15537
|
// src/modules/indexer/cache.ts
|
|
15211
|
-
import { readFileSync as readFileSync21, writeFileSync as writeFileSync13, existsSync as
|
|
15538
|
+
import { readFileSync as readFileSync21, writeFileSync as writeFileSync13, existsSync as existsSync34, mkdirSync as mkdirSync14, rmSync as rmSync2 } from "fs";
|
|
15212
15539
|
import { join as join24 } from "path";
|
|
15213
15540
|
|
|
15214
15541
|
class IndexCache {
|
|
@@ -15220,7 +15547,7 @@ class IndexCache {
|
|
|
15220
15547
|
load() {
|
|
15221
15548
|
if (this.cache)
|
|
15222
15549
|
return this.cache;
|
|
15223
|
-
if (!
|
|
15550
|
+
if (!existsSync34(this.cachePath))
|
|
15224
15551
|
return null;
|
|
15225
15552
|
try {
|
|
15226
15553
|
this.cache = JSON.parse(readFileSync21(this.cachePath, "utf-8"));
|
|
@@ -15232,13 +15559,13 @@ class IndexCache {
|
|
|
15232
15559
|
save(result) {
|
|
15233
15560
|
this.cache = result;
|
|
15234
15561
|
const dir = join24(this.cachePath, "..");
|
|
15235
|
-
if (!
|
|
15562
|
+
if (!existsSync34(dir))
|
|
15236
15563
|
mkdirSync14(dir, { recursive: true });
|
|
15237
15564
|
writeFileSync13(this.cachePath, JSON.stringify(result), "utf-8");
|
|
15238
15565
|
}
|
|
15239
15566
|
invalidate() {
|
|
15240
15567
|
this.cache = null;
|
|
15241
|
-
if (
|
|
15568
|
+
if (existsSync34(this.cachePath)) {
|
|
15242
15569
|
try {
|
|
15243
15570
|
rmSync2(this.cachePath);
|
|
15244
15571
|
} catch {}
|
|
@@ -15649,8 +15976,8 @@ __export(exports_bootstrap, {
|
|
|
15649
15976
|
bootstrap: () => bootstrap
|
|
15650
15977
|
});
|
|
15651
15978
|
import { homedir as homedir11 } from "os";
|
|
15652
|
-
import { join as join26, resolve as
|
|
15653
|
-
import { existsSync as
|
|
15979
|
+
import { join as join26, resolve as resolve18 } from "path";
|
|
15980
|
+
import { existsSync as existsSync35, readFileSync as readFileSync22, writeFileSync as writeFileSync14 } from "fs";
|
|
15654
15981
|
function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
15655
15982
|
const now = new Date().toISOString().replace("T", " ").slice(0, 19);
|
|
15656
15983
|
const isWin = profileCompressed.toLowerCase().includes("win32");
|
|
@@ -15669,9 +15996,9 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
|
15669
15996
|
`- DRY: Do not duplicate code, logic, or configuration — reuse existing utilities and patterns.`
|
|
15670
15997
|
];
|
|
15671
15998
|
if (isWin) {
|
|
15672
|
-
lines.push(``, `Windows environment — use Windows-compatible commands:`, `- Use "dir" instead of "ls". Use "dir /b" for bare listing.`, `- Use "type"
|
|
15999
|
+
lines.push(``, `Windows environment — use Windows-compatible commands (shell is cmd.exe, not PowerShell):`, `- Use "dir" instead of "ls". Use "dir /b" for bare listing. For file listings prefer the list_dir tool.`, `- Use "type" instead of "cat". For reading files prefer the read_file tool.`, `- Use "cd" instead of "pwd". Use "echo %cd%" to print working directory.`, `- Use "copy" instead of "cp", "move" instead of "mv", "del" instead of "rm".`, `- Do not use "mkdir -p" — Windows mkdir creates intermediate dirs by default. Use the create_dir tool instead.`, `- Do not use "head" or "tail" — they are Unix commands. Use the read_file tool with offset/limit instead.`, `- Do not use "grep" — use the grep tool instead. Do not use PowerShell cmdlets (Get-Content, Select-Object, cat) in bash — the shell is cmd.exe.`, `- Use forward slashes (/) or escaped backslashes (\\\\) in file paths.`, `- Your working directory is: ${baseDir}`);
|
|
15673
16000
|
}
|
|
15674
|
-
lines.push(``, `Bash tool rules:`, `- Use the "workdir" parameter to run commands in a specific directory. Prefer workdir over "cd dir && cmd" chaining.`, `- Run one command per tool call. Split multi-step shell operations into separate bash calls.`, `-
|
|
16001
|
+
lines.push(``, `Bash tool rules:`, `- Use the "workdir" parameter to run commands in a specific directory. Prefer workdir over "cd dir && cmd" chaining.`, `- Run one command per tool call. Split multi-step shell operations into separate bash calls.`, `- Dev servers, watchers and other long-running processes: pass "background: true" to get a process id immediately. Without it, any command still running after a few seconds is automatically moved to the background — check output with process_log.`);
|
|
15675
16002
|
lines.push(``, `=== DEVELOPMENT RULES — follow these strictly ===`, ``, `1. DEPENDENCIES FIRST: Before writing any source code, ALWAYS install project dependencies (e.g., "npm install", "pip install -r requirements.txt", "cargo build", "go mod tidy"). Verify the package manager's lock file or dependency directory exists. Never write code that imports/uses packages that aren't installed yet.`, `2. TOOLKIT/FWK FIRST: If the task specifies a framework or UI library, initialize and configure it BEFORE writing application code. Run its project init command first, then add components/modules. Never write your own version of what the framework already provides.`, `3. ONE STEP AT A TIME: Follow the plan sequentially. Complete step N before starting step N+1. When a step is done: verify the deliverables exist and have real content (not empty), then call "plan update step=N status=done". Do not redo completed work.`, `4. VERIFY YOUR WORK: After creating/modifying files, verify they exist on disk. After installing dependencies, verify the package manager completed successfully. After any command, check its output for errors. Don't assume operations succeeded.`, `5. NO PREMATURE WORK: Do not create files for future steps. Do not add imports/references to packages or modules that haven't been installed yet. Do not reference files or components that don't exist yet. Build incrementally — one layer at a time.`, `6. WHEN STUCK: If a command fails 2+ times, STOP and try a different approach. Write files directly instead of using commands. Ask the user for help. Never repeat the same failing command more than twice.`, ``, `=== PLAN QUALITY RULES — your plan MUST follow these ===`, ``, `- Each step must describe CONCRETE deliverables: exact filenames with paths, exact packages to install, exact CLI commands to run. Avoid vague steps — be specific.`, `- A step like "Настройка проекта" or "Setup the project" is too vague — describe what exactly needs to be configured or set up.`, `- A step like "Создать src/components/Header.tsx с навигацией и логотипом, добавить в src/App.tsx импорт <Header />" is GOOD.`, `- Include file extensions (.tsx, .css, .json) and directory paths. Every step must mention at least one file or command.`, `- The plan must cover EVERYTHING needed: init → deps → framework setup → code → verification.`, `- Number of steps: 5-8 for a typical task. Too few means you're being vague. Too many means you're over-splitting.`);
|
|
15676
16003
|
if (config.autoPlan) {
|
|
15677
16004
|
lines.push(``, `Plan rule (MANDATORY): For ANY task that requires creating files, installing packages, or multiple actions — you MUST create a plan using the "plan" tool BEFORE starting work. Each step must describe a concrete deliverable (specific files to create, packages to install, commands to run). Do not combine unrelated work into one step.`);
|
|
@@ -15728,7 +16055,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
15728
16055
|
retry: config.retry,
|
|
15729
16056
|
rateLimits: config.security?.rateLimits
|
|
15730
16057
|
});
|
|
15731
|
-
const baseDir = projectDir ?
|
|
16058
|
+
const baseDir = projectDir ? resolve18(projectDir) : process.cwd();
|
|
15732
16059
|
const projectMapCacheDir = join26(baseDir, ".mma");
|
|
15733
16060
|
const indexerModule = new IndexerModule({
|
|
15734
16061
|
baseDir,
|
|
@@ -15757,7 +16084,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
15757
16084
|
estimatedTokens: 250
|
|
15758
16085
|
};
|
|
15759
16086
|
const agentsMdGlobal = join26(dir, "AGENTS.md");
|
|
15760
|
-
if (!
|
|
16087
|
+
if (!existsSync35(agentsMdGlobal)) {
|
|
15761
16088
|
writeFileSync14(agentsMdGlobal, "", "utf-8");
|
|
15762
16089
|
}
|
|
15763
16090
|
const sessionDir = join26(dir, "sessions");
|
|
@@ -15797,12 +16124,23 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
15797
16124
|
timestamp: new Date().toISOString()
|
|
15798
16125
|
});
|
|
15799
16126
|
}
|
|
16127
|
+
},
|
|
16128
|
+
sessionLog: {
|
|
16129
|
+
plan: (event, detail, iteration) => {
|
|
16130
|
+
sessionManager.appendLog({
|
|
16131
|
+
ts: new Date().toISOString(),
|
|
16132
|
+
type: "plan",
|
|
16133
|
+
tool: event,
|
|
16134
|
+
content: detail,
|
|
16135
|
+
...iteration !== undefined ? { iteration } : {}
|
|
16136
|
+
});
|
|
16137
|
+
}
|
|
15800
16138
|
}
|
|
15801
16139
|
};
|
|
15802
16140
|
const toolExecutor = new ToolExecutor(toolRegistry, toolCtx, pluginManager);
|
|
15803
16141
|
toolCtx.llmProvider = llmProvider;
|
|
15804
16142
|
toolCtx.toolExecutor = toolExecutor;
|
|
15805
|
-
const hallucinationDetector = new HallucinationDetector;
|
|
16143
|
+
const hallucinationDetector = new HallucinationDetector(baseDir, llmProvider);
|
|
15806
16144
|
const moduleRegistry = new ModuleRegistry;
|
|
15807
16145
|
const execModule = new ExecutionModule(baseDir, config.stuckThreshold);
|
|
15808
16146
|
execModule.restorePlan();
|
|
@@ -15888,7 +16226,7 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete) {
|
|
|
15888
16226
|
join26(dir, "AGENTS.md")
|
|
15889
16227
|
];
|
|
15890
16228
|
for (const p of agentsMdCandidates) {
|
|
15891
|
-
if (
|
|
16229
|
+
if (existsSync35(p)) {
|
|
15892
16230
|
const content = readFileSync22(p, "utf-8").trim();
|
|
15893
16231
|
if (content) {
|
|
15894
16232
|
agentsMdBlocks.push({
|
|
@@ -16506,10 +16844,10 @@ function menuList(items) {
|
|
|
16506
16844
|
console.log(l);
|
|
16507
16845
|
}
|
|
16508
16846
|
function ask(rl, question, defaultValue) {
|
|
16509
|
-
return new Promise((
|
|
16847
|
+
return new Promise((resolve19) => {
|
|
16510
16848
|
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
|
16511
16849
|
rl.question(prompt, (answer) => {
|
|
16512
|
-
|
|
16850
|
+
resolve19(answer.trim() || defaultValue || "");
|
|
16513
16851
|
});
|
|
16514
16852
|
});
|
|
16515
16853
|
}
|
|
@@ -16728,12 +17066,12 @@ __export(exports_manifest, {
|
|
|
16728
17066
|
getCertMark: () => getCertMark,
|
|
16729
17067
|
MANIFEST_PATH: () => MANIFEST_PATH
|
|
16730
17068
|
});
|
|
16731
|
-
import { existsSync as
|
|
17069
|
+
import { existsSync as existsSync36, readFileSync as readFileSync23, mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "fs";
|
|
16732
17070
|
import { homedir as homedir13 } from "os";
|
|
16733
17071
|
import { join as join28 } from "path";
|
|
16734
17072
|
function readManifest(path = MANIFEST_PATH) {
|
|
16735
17073
|
try {
|
|
16736
|
-
if (
|
|
17074
|
+
if (existsSync36(path)) {
|
|
16737
17075
|
const raw = JSON.parse(readFileSync23(path, "utf-8"));
|
|
16738
17076
|
return { version: 1, certifications: raw.certifications ?? [] };
|
|
16739
17077
|
}
|
|
@@ -23899,7 +24237,7 @@ var init_scenarios = __esm(() => {
|
|
|
23899
24237
|
});
|
|
23900
24238
|
|
|
23901
24239
|
// src/modules/certification/loader.ts
|
|
23902
|
-
import { existsSync as
|
|
24240
|
+
import { existsSync as existsSync37, readdirSync as readdirSync10, readFileSync as readFileSync24 } from "fs";
|
|
23903
24241
|
import { join as join29 } from "path";
|
|
23904
24242
|
function validateScenario(s) {
|
|
23905
24243
|
const errors2 = [];
|
|
@@ -23949,7 +24287,7 @@ function loadScenarios(userDir) {
|
|
|
23949
24287
|
else
|
|
23950
24288
|
scenarios.push(s);
|
|
23951
24289
|
}
|
|
23952
|
-
if (userDir &&
|
|
24290
|
+
if (userDir && existsSync37(userDir)) {
|
|
23953
24291
|
for (const file of readdirSync10(userDir)) {
|
|
23954
24292
|
if (!file.endsWith(".yaml") && !file.endsWith(".yml"))
|
|
23955
24293
|
continue;
|
|
@@ -24007,7 +24345,7 @@ var init_loader3 = __esm(() => {
|
|
|
24007
24345
|
});
|
|
24008
24346
|
|
|
24009
24347
|
// src/modules/certification/fact-checker.ts
|
|
24010
|
-
import { existsSync as
|
|
24348
|
+
import { existsSync as existsSync38, readFileSync as readFileSync25, statSync as statSync7 } from "fs";
|
|
24011
24349
|
import { join as join30 } from "path";
|
|
24012
24350
|
function checkSandbox(sandboxDir, checks, exitCode, output) {
|
|
24013
24351
|
const failures = [];
|
|
@@ -24027,7 +24365,7 @@ function runCheck(sandboxDir, check, exitCode, output) {
|
|
|
24027
24365
|
case "fileExists":
|
|
24028
24366
|
return isFile(join30(sandboxDir, check.path));
|
|
24029
24367
|
case "fileNotExists":
|
|
24030
|
-
return !
|
|
24368
|
+
return !existsSync38(join30(sandboxDir, check.path));
|
|
24031
24369
|
case "dirExists":
|
|
24032
24370
|
return isDir(join30(sandboxDir, check.path));
|
|
24033
24371
|
case "fileContent": {
|
|
@@ -24053,14 +24391,14 @@ function runCheck(sandboxDir, check, exitCode, output) {
|
|
|
24053
24391
|
}
|
|
24054
24392
|
function isFile(p) {
|
|
24055
24393
|
try {
|
|
24056
|
-
return
|
|
24394
|
+
return existsSync38(p) && statSync7(p).isFile();
|
|
24057
24395
|
} catch {
|
|
24058
24396
|
return false;
|
|
24059
24397
|
}
|
|
24060
24398
|
}
|
|
24061
24399
|
function isDir(p) {
|
|
24062
24400
|
try {
|
|
24063
|
-
return
|
|
24401
|
+
return existsSync38(p) && statSync7(p).isDirectory();
|
|
24064
24402
|
} catch {
|
|
24065
24403
|
return false;
|
|
24066
24404
|
}
|
|
@@ -24090,10 +24428,10 @@ function describe(check) {
|
|
|
24090
24428
|
var init_fact_checker = () => {};
|
|
24091
24429
|
|
|
24092
24430
|
// src/modules/certification/runner.ts
|
|
24093
|
-
import { spawn as
|
|
24094
|
-
import { existsSync as
|
|
24095
|
-
import { platform as
|
|
24096
|
-
import { join as join31, resolve as
|
|
24431
|
+
import { spawn as spawn6 } from "child_process";
|
|
24432
|
+
import { existsSync as existsSync39, mkdirSync as mkdirSync16, rmSync as rmSync3, cpSync as cpSync2 } from "fs";
|
|
24433
|
+
import { platform as platform4 } from "os";
|
|
24434
|
+
import { join as join31, resolve as resolve19, dirname as dirname9 } from "path";
|
|
24097
24435
|
async function runScenario(scenario, opts) {
|
|
24098
24436
|
if (scenario.mode === "skip") {
|
|
24099
24437
|
return {
|
|
@@ -24174,7 +24512,7 @@ function prepareSandbox(sandbox, scenario, mmaRoot) {
|
|
|
24174
24512
|
mkdirSync16(sandbox, { recursive: true });
|
|
24175
24513
|
for (const f of scenario.fixtures ?? []) {
|
|
24176
24514
|
const src = join31(mmaRoot, f.source);
|
|
24177
|
-
if (!
|
|
24515
|
+
if (!existsSync39(src)) {
|
|
24178
24516
|
throw new Error(`fixture missing: ${f.source}`);
|
|
24179
24517
|
}
|
|
24180
24518
|
const dest = join31(sandbox, f.dest);
|
|
@@ -24184,27 +24522,27 @@ function prepareSandbox(sandbox, scenario, mmaRoot) {
|
|
|
24184
24522
|
}
|
|
24185
24523
|
function resolveMmaEntry(mmaRoot) {
|
|
24186
24524
|
const dev = join31(mmaRoot, "src", "cli", "main.ts");
|
|
24187
|
-
if (
|
|
24525
|
+
if (existsSync39(dev))
|
|
24188
24526
|
return dev;
|
|
24189
24527
|
return join31(mmaRoot, "dist", "main.js");
|
|
24190
24528
|
}
|
|
24191
24529
|
function findMmaRoot(fromDir) {
|
|
24192
24530
|
const candidates = [
|
|
24193
|
-
|
|
24194
|
-
|
|
24531
|
+
resolve19(fromDir, "..", "..", ".."),
|
|
24532
|
+
resolve19(fromDir, "..")
|
|
24195
24533
|
];
|
|
24196
24534
|
for (const c of candidates) {
|
|
24197
|
-
if (
|
|
24535
|
+
if (existsSync39(join31(c, "package.json")))
|
|
24198
24536
|
return c;
|
|
24199
24537
|
}
|
|
24200
24538
|
return process.cwd();
|
|
24201
24539
|
}
|
|
24202
|
-
function
|
|
24540
|
+
function killTree2(child) {
|
|
24203
24541
|
const pid = child.pid;
|
|
24204
24542
|
if (!pid)
|
|
24205
24543
|
return;
|
|
24206
|
-
if (
|
|
24207
|
-
|
|
24544
|
+
if (platform4() === "win32") {
|
|
24545
|
+
spawn6("taskkill", ["/pid", String(pid), "/T", "/F"], {
|
|
24208
24546
|
windowsHide: true,
|
|
24209
24547
|
stdio: "ignore"
|
|
24210
24548
|
});
|
|
@@ -24219,7 +24557,7 @@ function killTree3(child) {
|
|
|
24219
24557
|
}
|
|
24220
24558
|
}
|
|
24221
24559
|
var defaultRunner = (env2, cwd, args, timeoutMs) => new Promise((resolvePromise) => {
|
|
24222
|
-
const child =
|
|
24560
|
+
const child = spawn6(process.execPath, args, {
|
|
24223
24561
|
cwd,
|
|
24224
24562
|
env: env2,
|
|
24225
24563
|
windowsHide: true,
|
|
@@ -24236,7 +24574,7 @@ var defaultRunner = (env2, cwd, args, timeoutMs) => new Promise((resolvePromise)
|
|
|
24236
24574
|
});
|
|
24237
24575
|
const timer = setTimeout(() => {
|
|
24238
24576
|
timedOut = true;
|
|
24239
|
-
|
|
24577
|
+
killTree2(child);
|
|
24240
24578
|
}, timeoutMs);
|
|
24241
24579
|
child.on("close", (code) => {
|
|
24242
24580
|
clearTimeout(timer);
|
|
@@ -24264,13 +24602,13 @@ import { rmSync as rmSync4 } from "fs";
|
|
|
24264
24602
|
import { homedir as homedir14 } from "os";
|
|
24265
24603
|
import { join as join32, dirname as dirname10 } from "path";
|
|
24266
24604
|
import { fileURLToPath } from "url";
|
|
24267
|
-
import { existsSync as
|
|
24605
|
+
import { existsSync as existsSync40, readFileSync as readFileSync26 } from "fs";
|
|
24268
24606
|
function readVersion() {
|
|
24269
24607
|
const candidates = [
|
|
24270
24608
|
join32(MMA_ROOT, "package.json")
|
|
24271
24609
|
];
|
|
24272
24610
|
for (const p of candidates) {
|
|
24273
|
-
if (
|
|
24611
|
+
if (existsSync40(p)) {
|
|
24274
24612
|
const raw = JSON.parse(readFileSync26(p, "utf-8"));
|
|
24275
24613
|
if (raw.version)
|
|
24276
24614
|
return raw.version;
|
|
@@ -24425,7 +24763,7 @@ __export(exports_repl_commands, {
|
|
|
24425
24763
|
});
|
|
24426
24764
|
import { join as join34, dirname as dirname12 } from "path";
|
|
24427
24765
|
import { homedir as homedir16 } from "os";
|
|
24428
|
-
import { existsSync as
|
|
24766
|
+
import { existsSync as existsSync42, readFileSync as readFileSync28 } from "fs";
|
|
24429
24767
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
24430
24768
|
function readVersion3() {
|
|
24431
24769
|
const here = dirname12(fileURLToPath3(import.meta.url));
|
|
@@ -24434,7 +24772,7 @@ function readVersion3() {
|
|
|
24434
24772
|
join34(here, "..", "package.json")
|
|
24435
24773
|
];
|
|
24436
24774
|
for (const p of candidates) {
|
|
24437
|
-
if (
|
|
24775
|
+
if (existsSync42(p)) {
|
|
24438
24776
|
const raw = JSON.parse(readFileSync28(p, "utf8"));
|
|
24439
24777
|
if (raw.version)
|
|
24440
24778
|
return raw.version;
|
|
@@ -24498,8 +24836,8 @@ function registerMmaCommands(ctx) {
|
|
|
24498
24836
|
}
|
|
24499
24837
|
try {
|
|
24500
24838
|
const { loadFileAsDataUrl: loadFileAsDataUrl2, loadUrlAsDataUrl: loadUrlAsDataUrl2, readClipboardImage: readClipboardImage2 } = await Promise.resolve().then(() => (init_image_utils(), exports_image_utils));
|
|
24501
|
-
const { existsSync:
|
|
24502
|
-
const { resolve:
|
|
24839
|
+
const { existsSync: existsSync43 } = await import("fs");
|
|
24840
|
+
const { resolve: resolve20 } = await import("path");
|
|
24503
24841
|
let dataUrl;
|
|
24504
24842
|
let label;
|
|
24505
24843
|
if (source.toLowerCase() === "clipboard") {
|
|
@@ -24517,8 +24855,8 @@ function registerMmaCommands(ctx) {
|
|
|
24517
24855
|
dataUrl = result.dataUrl;
|
|
24518
24856
|
label = source;
|
|
24519
24857
|
} else {
|
|
24520
|
-
const absPath =
|
|
24521
|
-
if (!
|
|
24858
|
+
const absPath = resolve20(process.cwd(), source);
|
|
24859
|
+
if (!existsSync43(absPath)) {
|
|
24522
24860
|
console.log(pc.red(t("image.not_found", { path: source })));
|
|
24523
24861
|
return;
|
|
24524
24862
|
}
|
|
@@ -25055,7 +25393,7 @@ init_setup();
|
|
|
25055
25393
|
init_i18n();
|
|
25056
25394
|
import { join as join33, dirname as dirname11 } from "path";
|
|
25057
25395
|
import { homedir as homedir15 } from "os";
|
|
25058
|
-
import { existsSync as
|
|
25396
|
+
import { existsSync as existsSync41, readFileSync as readFileSync27 } from "fs";
|
|
25059
25397
|
|
|
25060
25398
|
// src/cli/security-commands.ts
|
|
25061
25399
|
init_bootstrap();
|
|
@@ -25683,7 +26021,7 @@ function readVersion2() {
|
|
|
25683
26021
|
join33(here, "..", "package.json")
|
|
25684
26022
|
];
|
|
25685
26023
|
for (const p of candidates) {
|
|
25686
|
-
if (
|
|
26024
|
+
if (existsSync41(p)) {
|
|
25687
26025
|
const raw = JSON.parse(readFileSync27(p, "utf8"));
|
|
25688
26026
|
if (raw.version)
|
|
25689
26027
|
return raw.version;
|
|
@@ -25910,7 +26248,7 @@ init_bootstrap();
|
|
|
25910
26248
|
// src/cli/repl.ts
|
|
25911
26249
|
init_colors();
|
|
25912
26250
|
import * as readline2 from "readline";
|
|
25913
|
-
import { existsSync as
|
|
26251
|
+
import { existsSync as existsSync43, readFileSync as readFileSync29, writeFileSync as writeFileSync16 } from "fs";
|
|
25914
26252
|
import { join as join35, dirname as dirname13 } from "path";
|
|
25915
26253
|
import { homedir as homedir17 } from "os";
|
|
25916
26254
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -26377,7 +26715,7 @@ function readVersion4() {
|
|
|
26377
26715
|
join35(here, "..", "package.json")
|
|
26378
26716
|
];
|
|
26379
26717
|
for (const p of candidates) {
|
|
26380
|
-
if (
|
|
26718
|
+
if (existsSync43(p)) {
|
|
26381
26719
|
const raw = JSON.parse(readFileSync29(p, "utf8"));
|
|
26382
26720
|
if (raw.version)
|
|
26383
26721
|
return raw.version;
|
|
@@ -26453,7 +26791,7 @@ class Repl {
|
|
|
26453
26791
|
this.setupListeners();
|
|
26454
26792
|
}
|
|
26455
26793
|
loadHistory() {
|
|
26456
|
-
if (
|
|
26794
|
+
if (existsSync43(this.historyPath)) {
|
|
26457
26795
|
try {
|
|
26458
26796
|
const raw = readFileSync29(this.historyPath, "utf-8");
|
|
26459
26797
|
this.history = raw.split(`
|
|
@@ -26767,7 +27105,7 @@ ${t("image.attached", { source: "clipboard", size: `${sizeKb} KB` })}`));
|
|
|
26767
27105
|
join35(this.baseDir, ".mma", "AGENTS.md"),
|
|
26768
27106
|
join35(this.configDir, "AGENTS.md")
|
|
26769
27107
|
];
|
|
26770
|
-
const foundAgents = agentsMdCandidates.filter((p) =>
|
|
27108
|
+
const foundAgents = agentsMdCandidates.filter((p) => existsSync43(p));
|
|
26771
27109
|
if (foundAgents.length > 0) {
|
|
26772
27110
|
for (const p of foundAgents) {
|
|
26773
27111
|
row(t("repl.agents_label"), pc.dim(p));
|
|
@@ -26804,7 +27142,7 @@ init_setup();
|
|
|
26804
27142
|
init_config2();
|
|
26805
27143
|
init_i18n();
|
|
26806
27144
|
init_colors();
|
|
26807
|
-
import { existsSync as
|
|
27145
|
+
import { existsSync as existsSync44 } from "fs";
|
|
26808
27146
|
import { join as join36 } from "path";
|
|
26809
27147
|
import { homedir as homedir18 } from "os";
|
|
26810
27148
|
async function main() {
|
|
@@ -26870,7 +27208,7 @@ async function main() {
|
|
|
26870
27208
|
agent.shutdown();
|
|
26871
27209
|
} else {
|
|
26872
27210
|
const configPath = join36(homedir18(), ".mma", "config.json");
|
|
26873
|
-
if (!
|
|
27211
|
+
if (!existsSync44(configPath)) {
|
|
26874
27212
|
console.log(pc.yellow(`
|
|
26875
27213
|
` + t("cli.first_run") + `
|
|
26876
27214
|
`));
|