@menteeai/menteeswe 0.1.19 → 0.1.21
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/cli.js +107 -40
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -751,39 +751,93 @@ function recentFile(cwd) {
|
|
|
751
751
|
const hash = crypto3.createHash("sha1").update(cwd).digest("hex").slice(0, 16);
|
|
752
752
|
return path8.join(sessionsDir(), `${hash}.files.json`);
|
|
753
753
|
}
|
|
754
|
-
function
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
754
|
+
function fileHash(content) {
|
|
755
|
+
return crypto3.createHash("sha1").update(content).digest("hex").slice(0, 12);
|
|
756
|
+
}
|
|
757
|
+
function normalizeRecords(value) {
|
|
758
|
+
if (!Array.isArray(value)) return [];
|
|
759
|
+
const out = [];
|
|
760
|
+
for (const item of value) {
|
|
761
|
+
if (typeof item === "string") out.push({ path: item });
|
|
762
|
+
else if (item && typeof item === "object" && typeof item.path === "string") {
|
|
763
|
+
out.push(item);
|
|
761
764
|
}
|
|
762
|
-
const merge = (known, fresh) => [.../* @__PURE__ */ new Set([...fresh, ...known])].slice(0, 15);
|
|
763
|
-
fs9.writeFileSync(
|
|
764
|
-
recentFile(cwd),
|
|
765
|
-
JSON.stringify({ reads: merge(prev.reads, reads), writes: merge(prev.writes, writes) }, null, 2)
|
|
766
|
-
);
|
|
767
|
-
} catch {
|
|
768
765
|
}
|
|
766
|
+
return out;
|
|
769
767
|
}
|
|
770
|
-
function
|
|
768
|
+
function loadRecentFiles(cwd) {
|
|
771
769
|
try {
|
|
772
770
|
const parsed = JSON.parse(fs9.readFileSync(recentFile(cwd), "utf8"));
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
771
|
+
return { reads: normalizeRecords(parsed.reads), writes: normalizeRecords(parsed.writes) };
|
|
772
|
+
} catch {
|
|
773
|
+
return { reads: [], writes: [] };
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
function saveRecentFiles(cwd, reads, writes, hashes) {
|
|
777
|
+
try {
|
|
778
|
+
const prev = loadRecentFiles(cwd);
|
|
779
|
+
const absHash = (rel) => hashes?.get(path8.resolve(cwd, rel));
|
|
780
|
+
const merge = (known, fresh) => {
|
|
781
|
+
const byPath = /* @__PURE__ */ new Map();
|
|
782
|
+
for (const rel of fresh) {
|
|
783
|
+
byPath.set(rel, { path: rel, hash: absHash(rel) });
|
|
784
|
+
}
|
|
785
|
+
for (const rec of known) {
|
|
786
|
+
if (!byPath.has(rec.path)) byPath.set(rec.path, rec);
|
|
787
|
+
}
|
|
788
|
+
return [...byPath.values()].slice(0, 15);
|
|
789
|
+
};
|
|
790
|
+
fs9.writeFileSync(
|
|
791
|
+
recentFile(cwd),
|
|
792
|
+
JSON.stringify({ reads: merge(prev.reads, reads), writes: merge(prev.writes, writes) }, null, 2)
|
|
781
793
|
);
|
|
782
|
-
return lines.join("\n");
|
|
783
794
|
} catch {
|
|
784
|
-
return "";
|
|
785
795
|
}
|
|
786
796
|
}
|
|
797
|
+
function formatRecentFiles(cwd, prewarm) {
|
|
798
|
+
const stored = loadRecentFiles(cwd);
|
|
799
|
+
if (stored.reads.length === 0 && stored.writes.length === 0) return "";
|
|
800
|
+
const verify = (records) => {
|
|
801
|
+
const unchanged = [];
|
|
802
|
+
const changed = [];
|
|
803
|
+
const legacy = [];
|
|
804
|
+
for (const rec of records.slice(0, 15)) {
|
|
805
|
+
if (!rec.hash) {
|
|
806
|
+
legacy.push(rec);
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
try {
|
|
810
|
+
const content = fs9.readFileSync(path8.resolve(cwd, rec.path), "utf8");
|
|
811
|
+
const current = fileHash(content);
|
|
812
|
+
if (current === rec.hash) {
|
|
813
|
+
unchanged.push(rec);
|
|
814
|
+
prewarm?.set(path8.resolve(cwd, rec.path), current);
|
|
815
|
+
} else {
|
|
816
|
+
changed.push(rec);
|
|
817
|
+
}
|
|
818
|
+
} catch {
|
|
819
|
+
changed.push(rec);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return { unchanged, changed, legacy };
|
|
823
|
+
};
|
|
824
|
+
const w = verify(stored.writes);
|
|
825
|
+
const writePaths = new Set(stored.writes.map((r2) => r2.path));
|
|
826
|
+
const r = verify(stored.reads.filter((rec) => !writePaths.has(rec.path)));
|
|
827
|
+
const lines = ["# Known files from earlier tasks in this project"];
|
|
828
|
+
const join = (list) => list.map((rec) => rec.path).join(", ");
|
|
829
|
+
if (w.unchanged.length > 0) lines.push(`Modified recently, verified unchanged on disk: ${join(w.unchanged)}`);
|
|
830
|
+
if (r.unchanged.length > 0) lines.push(`Read recently, verified unchanged on disk: ${join(r.unchanged)}`);
|
|
831
|
+
const stale = [...w.changed, ...r.changed];
|
|
832
|
+
if (stale.length > 0) lines.push(`Changed (or missing) on disk since then \u2014 re-read before editing: ${join(stale)}`);
|
|
833
|
+
for (const rec of [...w.legacy, ...r.legacy]) {
|
|
834
|
+
lines.push(`Known from earlier tasks (state unknown): ${rec.path}`);
|
|
835
|
+
}
|
|
836
|
+
lines.push(
|
|
837
|
+
"For files verified unchanged you may apply_patch directly without re-reading; the harness validates the hash and will tell you if the file changed. Do not re-search or re-read verified files unless needed."
|
|
838
|
+
);
|
|
839
|
+
return lines.join("\n");
|
|
840
|
+
}
|
|
787
841
|
var init_conversation = __esm({
|
|
788
842
|
"src/agent/conversation.ts"() {
|
|
789
843
|
"use strict";
|
|
@@ -843,13 +897,15 @@ function isRateLimitError(error) {
|
|
|
843
897
|
}
|
|
844
898
|
return /\b429\b|max rpm|rate limit|too many requests/i.test(error.message);
|
|
845
899
|
}
|
|
846
|
-
async function generateWithRetry(provider, model, system, messages, tools, bus, onToken) {
|
|
900
|
+
async function generateWithRetry(provider, model, system, messages, tools, bus, onToken, signal) {
|
|
847
901
|
let lastError = null;
|
|
848
902
|
for (let attempt = 0; attempt <= RATE_LIMIT_MAX_ATTEMPTS; attempt++) {
|
|
903
|
+
if (signal?.aborted) throw new Error("Aborted by user");
|
|
849
904
|
try {
|
|
850
|
-
return await provider.generate({ system, messages, tools: tools.schemas(), onToken }, model);
|
|
905
|
+
return await provider.generate({ system, messages, tools: tools.schemas(), onToken, signal }, model);
|
|
851
906
|
} catch (error) {
|
|
852
907
|
lastError = error;
|
|
908
|
+
if (signal?.aborted) throw lastError;
|
|
853
909
|
if (attempt >= RATE_LIMIT_MAX_ATTEMPTS || !isRateLimitError(lastError)) {
|
|
854
910
|
throw lastError;
|
|
855
911
|
}
|
|
@@ -935,8 +991,9 @@ async function runAgent(options) {
|
|
|
935
991
|
signal
|
|
936
992
|
} = options;
|
|
937
993
|
const state = createAgentState(task, maxIterations);
|
|
994
|
+
const fileState = /* @__PURE__ */ new Map();
|
|
938
995
|
const prior = formatConversationContext(cwd);
|
|
939
|
-
const knownFiles = formatRecentFiles(cwd);
|
|
996
|
+
const knownFiles = formatRecentFiles(cwd, fileState);
|
|
940
997
|
const system = buildSystemPrompt(cwd) + (knownFiles ? `
|
|
941
998
|
|
|
942
999
|
${knownFiles}` : "") + (prior ? `
|
|
@@ -951,7 +1008,7 @@ ${systemExtra}` : "");
|
|
|
951
1008
|
const toolCtx = {
|
|
952
1009
|
cwd,
|
|
953
1010
|
approval,
|
|
954
|
-
fileState
|
|
1011
|
+
fileState,
|
|
955
1012
|
emit: (type, message, data) => {
|
|
956
1013
|
bus.emit(type, message, data);
|
|
957
1014
|
}
|
|
@@ -990,9 +1047,13 @@ ${systemExtra}` : "");
|
|
|
990
1047
|
};
|
|
991
1048
|
let response;
|
|
992
1049
|
try {
|
|
993
|
-
response = await generateWithRetry(provider, model, system, messages, tools, bus, onToken);
|
|
1050
|
+
response = await generateWithRetry(provider, model, system, messages, tools, bus, onToken, signal);
|
|
994
1051
|
} catch (error) {
|
|
995
1052
|
flushThink();
|
|
1053
|
+
if (signal?.aborted) {
|
|
1054
|
+
finalText = "Task cancelled.";
|
|
1055
|
+
break;
|
|
1056
|
+
}
|
|
996
1057
|
const message = error.message;
|
|
997
1058
|
let hint = "";
|
|
998
1059
|
if (/insufficient balance|no resource package|recharge/i.test(message)) {
|
|
@@ -1003,7 +1064,7 @@ ${systemExtra}` : "");
|
|
|
1003
1064
|
bus.emit("error", message + hint);
|
|
1004
1065
|
finalText = `Model request failed: ${message}`;
|
|
1005
1066
|
appendTurn(cwd, task, finalText);
|
|
1006
|
-
saveRecentFiles(cwd, state.readFiles, state.modifiedFiles);
|
|
1067
|
+
saveRecentFiles(cwd, state.readFiles, state.modifiedFiles, fileState);
|
|
1007
1068
|
bus.emit("task_completed", finalText.slice(0, 400), {
|
|
1008
1069
|
success: false,
|
|
1009
1070
|
finalText,
|
|
@@ -1076,6 +1137,7 @@ Investigate the root cause, fix it, then finish with a final answer.`
|
|
|
1076
1137
|
bus.emit("info", response.content.trim().slice(0, 300));
|
|
1077
1138
|
}
|
|
1078
1139
|
for (const call of response.toolCalls) {
|
|
1140
|
+
if (signal?.aborted) break;
|
|
1079
1141
|
const tool = tools.get(call.function.name);
|
|
1080
1142
|
if (!tool) {
|
|
1081
1143
|
messages.push({
|
|
@@ -1208,7 +1270,7 @@ Investigate the root cause, fix it, then finish with a final answer.`
|
|
|
1208
1270
|
const success = sawFinish && state.errors.length === 0;
|
|
1209
1271
|
if (finalText && !signal?.aborted) {
|
|
1210
1272
|
appendTurn(cwd, task, finalText);
|
|
1211
|
-
saveRecentFiles(cwd, state.readFiles, state.modifiedFiles);
|
|
1273
|
+
saveRecentFiles(cwd, state.readFiles, state.modifiedFiles, fileState);
|
|
1212
1274
|
}
|
|
1213
1275
|
bus.emit("task_completed", finalText.slice(0, 400), {
|
|
1214
1276
|
success,
|
|
@@ -1244,7 +1306,7 @@ var init_loop = __esm({
|
|
|
1244
1306
|
var version;
|
|
1245
1307
|
var init_package = __esm({
|
|
1246
1308
|
"package.json"() {
|
|
1247
|
-
version = "0.1.
|
|
1309
|
+
version = "0.1.21";
|
|
1248
1310
|
}
|
|
1249
1311
|
});
|
|
1250
1312
|
|
|
@@ -2197,16 +2259,20 @@ var OpenAICompatProvider = class {
|
|
|
2197
2259
|
...request.temperature !== void 0 ? { temperature: request.temperature } : {},
|
|
2198
2260
|
...request.maxTokens ? { max_tokens: request.maxTokens } : {}
|
|
2199
2261
|
};
|
|
2262
|
+
const reqOptions = request.signal ? { signal: request.signal } : void 0;
|
|
2200
2263
|
if (!request.onToken) {
|
|
2201
|
-
const response2 = await this.client.chat.completions.create(baseParams);
|
|
2264
|
+
const response2 = await this.client.chat.completions.create(baseParams, reqOptions);
|
|
2202
2265
|
return parseResponse(response2);
|
|
2203
2266
|
}
|
|
2204
2267
|
const streamOnce = async (withUsage) => {
|
|
2205
|
-
const stream = await this.client.chat.completions.create(
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2268
|
+
const stream = await this.client.chat.completions.create(
|
|
2269
|
+
{
|
|
2270
|
+
...baseParams,
|
|
2271
|
+
stream: true,
|
|
2272
|
+
...withUsage ? { stream_options: { include_usage: true } } : {}
|
|
2273
|
+
},
|
|
2274
|
+
reqOptions
|
|
2275
|
+
);
|
|
2210
2276
|
let content = "";
|
|
2211
2277
|
let finishReason = "stop";
|
|
2212
2278
|
let usage;
|
|
@@ -2257,7 +2323,7 @@ var OpenAICompatProvider = class {
|
|
|
2257
2323
|
} catch {
|
|
2258
2324
|
}
|
|
2259
2325
|
}
|
|
2260
|
-
const response = await this.client.chat.completions.create(baseParams);
|
|
2326
|
+
const response = await this.client.chat.completions.create(baseParams, reqOptions);
|
|
2261
2327
|
const parsed = parseResponse(response);
|
|
2262
2328
|
if (parsed.content) {
|
|
2263
2329
|
for (let i = 0; i < parsed.content.length; i += 3) {
|
|
@@ -3838,7 +3904,8 @@ program.argument("[task...]", "the software task to perform (omit to type it int
|
|
|
3838
3904
|
yes: options.yes === true,
|
|
3839
3905
|
maxIterations
|
|
3840
3906
|
}
|
|
3841
|
-
)
|
|
3907
|
+
),
|
|
3908
|
+
{ exitOnCtrlC: false }
|
|
3842
3909
|
);
|
|
3843
3910
|
await waitUntilExit();
|
|
3844
3911
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@menteeai/menteeswe",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.21",
|
|
4
4
|
"description": "MenteE SWE — a model-agnostic autonomous software-engineering agent CLI. Bring your own intelligence: Kimi, GLM/Z.ai, and more.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|